├── app ├── Filters │ └── .gitkeep ├── Helpers │ └── .gitkeep ├── Language │ ├── .gitkeep │ └── en │ │ └── Validation.php ├── Libraries │ └── .gitkeep ├── Models │ ├── .gitkeep │ ├── AjaxModel.php │ ├── ObjectModel.php │ └── BuilderModel.php ├── ThirdParty │ └── .gitkeep ├── Database │ ├── Seeds │ │ └── .gitkeep │ └── Migrations │ │ └── .gitkeep ├── Config │ ├── ForeignCharacters.php │ ├── Images.php │ ├── Honeypot.php │ ├── Boot │ │ ├── production.php │ │ ├── testing.php │ │ └── development.php │ ├── Services.php │ ├── Validation.php │ ├── Pager.php │ ├── Encryption.php │ ├── Filters.php │ ├── View.php │ ├── Exceptions.php │ ├── Modules.php │ ├── Migrations.php │ ├── Events.php │ ├── Kint.php │ ├── ContentSecurityPolicy.php │ ├── Autoload.php │ ├── Routes.php │ ├── DocTypes.php │ ├── Toolbar.php │ ├── Format.php │ ├── Paths.php │ ├── Database.php │ ├── Email.php │ ├── Constants.php │ ├── Cache.php │ ├── Logger.php │ ├── UserAgents.php │ └── App.php ├── .htaccess ├── Views │ ├── errors │ │ ├── cli │ │ │ ├── error_404.php │ │ │ ├── production.php │ │ │ └── error_exception.php │ │ └── html │ │ │ ├── production.php │ │ │ ├── error_404.php │ │ │ ├── debug.css │ │ │ └── debug.js │ ├── export │ │ └── pdf.php │ ├── layout │ │ ├── sidebar.php │ │ └── templates.php │ ├── ajax │ │ ├── index.php │ │ ├── view_data.php │ │ ├── view_modal.php │ │ └── view_modal_edit.php │ ├── dashboard │ │ └── index.php │ └── objects │ │ ├── form_add.php │ │ ├── form_update.php │ │ └── index.php ├── index.html ├── Controllers │ ├── Home.php │ ├── Dashboard.php │ ├── BaseController.php │ ├── Builder.php │ ├── Export.php │ ├── Objects.php │ └── Ajax.php └── Common.php ├── writable ├── photoindex.html ├── photo │ ├── 1609648185_c4e3e1fdbd453680d53d.png │ ├── 1609648289_1b5b1dbd19d9a09eff4f.png │ ├── 1609648548_e1a56014f0d182122ed3.png │ └── 1609648622_442519b223b274aecf6f.png ├── .htaccess ├── cache │ └── index.html ├── logs │ └── index.html ├── session │ └── index.html └── uploads │ └── index.html ├── public ├── robots.txt ├── favicon.ico ├── assets │ ├── demo │ │ ├── datatables-demo.js │ │ ├── chart-pie-demo.js │ │ ├── chart-bar-demo.js │ │ └── chart-area-demo.js │ └── img │ │ └── error-404-monochrome.svg ├── photos │ ├── 1609652617_aefcf01489903d3b1a03.jpeg │ └── 1609654226_b43f308ecb2f7d206226.jpeg ├── js │ └── scripts.js ├── index.php └── .htaccess ├── tests ├── session │ └── ExampleSessionTest.php ├── _support │ ├── Models │ │ └── ExampleModel.php │ ├── SessionTestCase.php │ ├── Database │ │ ├── Seeds │ │ │ └── ExampleSeeder.php │ │ └── Migrations │ │ │ └── 2020-02-22-222222_example_migration.php │ ├── DatabaseTestCase.php │ └── Libraries │ │ └── ConfigReader.php ├── unit │ └── HealthTest.php ├── database │ └── ExampleDatabaseTest.php └── README.md ├── composer.json ├── license.txt ├── README.md ├── .github └── workflows │ └── phpunit.yml ├── spark ├── phpunit.xml.dist ├── .gitignore └── env /app/Filters/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/Helpers/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/Language/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/Libraries/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/Models/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/ThirdParty/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/Database/Seeds/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /writable/photoindex.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/Database/Migrations/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: 3 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ilhamlutfi/starter-ci4/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /app/Config/ForeignCharacters.php: -------------------------------------------------------------------------------- 1 | 2 | Require all denied 3 | 4 | 5 | Deny from all 6 | 7 | -------------------------------------------------------------------------------- /app/Language/en/Validation.php: -------------------------------------------------------------------------------- 1 | 2 | Require all denied 3 | 4 | 5 | Deny from all 6 | 7 | -------------------------------------------------------------------------------- /app/Views/errors/cli/production.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 403 Forbidden 5 | 6 | 7 | 8 |

Directory access is forbidden.

9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /writable/cache/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 403 Forbidden 5 | 6 | 7 | 8 |

Directory access is forbidden.

9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /writable/logs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 403 Forbidden 5 | 6 | 7 | 8 |

Directory access is forbidden.

9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /writable/session/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 403 Forbidden 5 | 6 | 7 | 8 |

Directory access is forbidden.

9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /writable/uploads/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 403 Forbidden 5 | 6 | 7 | 8 |

Directory access is forbidden.

9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /app/Controllers/Home.php: -------------------------------------------------------------------------------- 1 | 'Starter Project CodeIgniter 4' 11 | ]; 12 | 13 | return view('dashboard/index', $data); 14 | } 15 | } -------------------------------------------------------------------------------- /tests/session/ExampleSessionTest.php: -------------------------------------------------------------------------------- 1 | session->set('logged_in', 123); 13 | 14 | $value = $this->session->get('logged_in'); 15 | 16 | $this->assertEquals(123, $value); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /app/Models/AjaxModel.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Whoops! 8 | 9 | 12 | 13 | 14 | 15 |
16 | 17 |

Whoops!

18 | 19 |

We seem to have hit a snag. Please try again later...

20 | 21 |
22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /tests/_support/Models/ExampleModel.php: -------------------------------------------------------------------------------- 1 | 4 | Message: 5 | Filename: getFile(), "\n"; ?> 6 | Line Number: getLine(); ?> 7 | 8 | 9 | 10 | Backtrace: 11 | getTrace() as $error): ?> 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /public/assets/demo/chart-pie-demo.js: -------------------------------------------------------------------------------- 1 | // Set new default font family and font color to mimic Bootstrap's default styling 2 | Chart.defaults.global.defaultFontFamily = '-apple-system,system-ui,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif'; 3 | Chart.defaults.global.defaultFontColor = '#292b2c'; 4 | 5 | // Pie Chart Example 6 | var ctx = document.getElementById("myPieChart"); 7 | var myPieChart = new Chart(ctx, { 8 | type: 'pie', 9 | data: { 10 | labels: ["Blue", "Red", "Yellow", "Green"], 11 | datasets: [{ 12 | data: [12.21, 15.58, 11.25, 8.32], 13 | backgroundColor: ['#007bff', '#dc3545', '#ffc107', '#28a745'], 14 | }], 15 | }, 16 | }); 17 | -------------------------------------------------------------------------------- /tests/unit/HealthTest.php: -------------------------------------------------------------------------------- 1 | assertTrue($test); 15 | } 16 | 17 | public function testBaseUrlHasBeenSet() 18 | { 19 | $env = $config = false; 20 | 21 | // First check in .env 22 | if (is_file(HOMEPATH . '.env')) 23 | { 24 | $env = (bool) preg_grep("/^app\.baseURL = './", file(HOMEPATH . '.env')); 25 | } 26 | 27 | // Then check the actual config file 28 | $reader = new \Tests\Support\Libraries\ConfigReader(); 29 | $config = ! empty($reader->baseUrl); 30 | 31 | $this->assertTrue($env || $config); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /app/Config/Images.php: -------------------------------------------------------------------------------- 1 | \CodeIgniter\Images\Handlers\GDHandler::class, 29 | 'imagick' => \CodeIgniter\Images\Handlers\ImageMagickHandler::class, 30 | ]; 31 | } 32 | -------------------------------------------------------------------------------- /tests/_support/SessionTestCase.php: -------------------------------------------------------------------------------- 1 | mockSession(); 19 | } 20 | 21 | /** 22 | * Pre-loads the mock session driver into $this->session. 23 | * 24 | * @var string 25 | */ 26 | protected function mockSession() 27 | { 28 | $config = config('App'); 29 | $this->session = new MockSession(new ArrayHandler($config, '0.0.0.0'), $config); 30 | \Config\Services::injectMock('session', $this->session); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /app/Config/Honeypot.php: -------------------------------------------------------------------------------- 1 | {label}'; 35 | 36 | /** 37 | * Honeypot container 38 | * 39 | * @var string 40 | */ 41 | public $container = '
{template}
'; 42 | } 43 | -------------------------------------------------------------------------------- /public/js/scripts.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Start Bootstrap - SB Admin v6.0.1 (https://startbootstrap.com/templates/sb-admin) 3 | * Copyright 2013-2020 Start Bootstrap 4 | * Licensed under MIT (https://github.com/StartBootstrap/startbootstrap-sb-admin/blob/master/LICENSE) 5 | */ 6 | (function($) { 7 | "use strict"; 8 | 9 | // Add active state to sidbar nav links 10 | var path = window.location.href; // because the 'href' property of the DOM element is the absolute path 11 | $("#layoutSidenav_nav .sb-sidenav a.nav-link").each(function() { 12 | if (this.href === path) { 13 | $(this).addClass("active"); 14 | } 15 | }); 16 | 17 | // Toggle the side navigation 18 | $("#sidebarToggle").on("click", function(e) { 19 | e.preventDefault(); 20 | $("body").toggleClass("sb-sidenav-toggled"); 21 | }); 22 | })(jQuery); 23 | -------------------------------------------------------------------------------- /app/Config/Boot/production.php: -------------------------------------------------------------------------------- 1 | =7.2", 11 | "codeigniter4/framework": "^4", 12 | "tecnickcom/tcpdf": "^6.3", 13 | "phpoffice/phpspreadsheet": "^1.16", 14 | "myth/auth": "^1.0@beta" 15 | }, 16 | "require-dev": { 17 | "fzaninotto/faker": "^1.9@dev", 18 | "mikey179/vfsstream": "1.6.*", 19 | "phpunit/phpunit": "^8.5" 20 | }, 21 | "autoload-dev": { 22 | "psr-4": { 23 | "Tests\\Support\\": "tests/_support" 24 | } 25 | }, 26 | "scripts": { 27 | "post-update-cmd": [ 28 | "@composer dump-autoload" 29 | ], 30 | "test": "phpunit" 31 | }, 32 | "support": { 33 | "forum": "http://forum.codeigniter.com/", 34 | "source": "https://github.com/codeigniter4/CodeIgniter4", 35 | "slack": "https://codeigniterchat.slack.com" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /app/Config/Services.php: -------------------------------------------------------------------------------- 1 | 'CodeIgniter\Validation\Views\list', 31 | 'single' => 'CodeIgniter\Validation\Views\single', 32 | ]; 33 | 34 | //-------------------------------------------------------------------- 35 | // Rules 36 | //-------------------------------------------------------------------- 37 | } 38 | -------------------------------------------------------------------------------- /tests/_support/Database/Seeds/ExampleSeeder.php: -------------------------------------------------------------------------------- 1 | 'Test Factory', 12 | 'uid' => 'test001', 13 | 'class' => 'Factories\Tests\NewFactory', 14 | 'icon' => 'fas fa-puzzle-piece', 15 | 'summary' => 'Longer sample text for testing', 16 | ], 17 | [ 18 | 'name' => 'Widget Factory', 19 | 'uid' => 'widget', 20 | 'class' => 'Factories\Tests\WidgetPlant', 21 | 'icon' => 'fas fa-puzzle-piece', 22 | 'summary' => 'Create widgets in your factory', 23 | ], 24 | [ 25 | 'name' => 'Evil Factory', 26 | 'uid' => 'evil-maker', 27 | 'class' => 'Factories\Evil\MyFactory', 28 | 'icon' => 'fas fa-book-dead', 29 | 'summary' => 'Abandon all hope, ye who enter here', 30 | ], 31 | ]; 32 | 33 | $builder = $this->db->table('factories'); 34 | 35 | foreach ($factories as $factory) 36 | { 37 | $builder->insert($factory); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /tests/database/ExampleDatabaseTest.php: -------------------------------------------------------------------------------- 1 | findAll(); 20 | 21 | // Make sure the count is as expected 22 | $this->assertCount(3, $objects); 23 | } 24 | 25 | public function testSoftDeleteLeavesRow() 26 | { 27 | $model = new ExampleModel(); 28 | $this->setPrivateProperty($model, 'useSoftDeletes', true); 29 | $this->setPrivateProperty($model, 'tempUseSoftDeletes', true); 30 | 31 | $object = $model->first(); 32 | $model->delete($object->id); 33 | 34 | // The model should no longer find it 35 | $this->assertNull($model->find($object->id)); 36 | 37 | // ... but it should still be in the database 38 | $result = $model->builder()->where('id', $object->id)->get()->getResult(); 39 | 40 | $this->assertCount(1, $result); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /app/Config/Pager.php: -------------------------------------------------------------------------------- 1 | 'CodeIgniter\Pager\Views\default_full', 22 | 'default_simple' => 'CodeIgniter\Pager\Views\default_simple', 23 | 'default_head' => 'CodeIgniter\Pager\Views\default_head', 24 | ]; 25 | 26 | /* 27 | |-------------------------------------------------------------------------- 28 | | Items Per Page 29 | |-------------------------------------------------------------------------- 30 | | 31 | | The default number of results shown in a single page. 32 | | 33 | */ 34 | public $perPage = 20; 35 | } 36 | -------------------------------------------------------------------------------- /app/Config/Encryption.php: -------------------------------------------------------------------------------- 1 | db = \Config\Database::connect(); 14 | $this->builder = $this->db->table('employees'); // tabel employees 15 | } 16 | 17 | // func select all data or by id 18 | public function select_data($id = FALSE) 19 | { 20 | if ($id == FALSE) { 21 | return $this->builder->get()->getResultObject(); 22 | } 23 | 24 | return $this->builder->getWhere(['id' => $id])->getRow(); 25 | } 26 | 27 | // func insert data to db 28 | public function add_data($data) 29 | { 30 | $this->builder->insert($data); 31 | } 32 | 33 | // func delete data from db 34 | public function delete_data($id) 35 | { 36 | $this->builder->where('id', $id); 37 | $this->builder->delete(); 38 | } 39 | 40 | // func update data from db 41 | public function update_data($id, $data) 42 | { 43 | $this->builder->where('id', $id); 44 | $this->builder->update($data); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /license.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014-2019 British Columbia Institute of Technology 4 | Copyright (c) 2019-2020 CodeIgniter Foundation 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /app/Config/Filters.php: -------------------------------------------------------------------------------- 1 | \CodeIgniter\Filters\CSRF::class, 11 | 'toolbar' => \CodeIgniter\Filters\DebugToolbar::class, 12 | 'honeypot' => \CodeIgniter\Filters\Honeypot::class, 13 | 'login' => \Myth\Auth\Filters\LoginFilter::class, 14 | 'role' => \Myth\Auth\Filters\RoleFilter::class, 15 | 'permission' => \Myth\Auth\Filters\PermissionFilter::class, 16 | 17 | ]; 18 | 19 | // Always applied before every request 20 | public $globals = [ 21 | 'before' => [ 22 | 'honeypot', 23 | 'login', 24 | // 'csrf' 25 | ], 26 | 'after' => [ 27 | 'toolbar', 28 | //'honeypot' 29 | ], 30 | ]; 31 | 32 | // Works on all of a particular HTTP method 33 | // (GET, POST, etc) as BEFORE filters only 34 | // like: 'post' => ['CSRF', 'throttle'], 35 | public $methods = []; 36 | 37 | // List filter aliases and any before/after uri patterns 38 | // that they should run on, like: 39 | // 'isLoggedIn' => ['before' => ['account/*', 'profiles/*']], 40 | public $filters = []; 41 | } 42 | -------------------------------------------------------------------------------- /app/Config/View.php: -------------------------------------------------------------------------------- 1 | [ 13 | 'type' => 'varchar', 14 | 'constraint' => 31, 15 | ], 16 | 'uid' => [ 17 | 'type' => 'varchar', 18 | 'constraint' => 31, 19 | ], 20 | 'class' => [ 21 | 'type' => 'varchar', 22 | 'constraint' => 63, 23 | ], 24 | 'icon' => [ 25 | 'type' => 'varchar', 26 | 'constraint' => 31, 27 | ], 28 | 'summary' => [ 29 | 'type' => 'varchar', 30 | 'constraint' => 255, 31 | ], 32 | 'created_at' => [ 33 | 'type' => 'datetime', 34 | 'null' => true, 35 | ], 36 | 'updated_at' => [ 37 | 'type' => 'datetime', 38 | 'null' => true, 39 | ], 40 | 'deleted_at' => [ 41 | 'type' => 'datetime', 42 | 'null' => true, 43 | ], 44 | ]; 45 | 46 | $this->forge->addField('id'); 47 | $this->forge->addField($fields); 48 | 49 | $this->forge->addKey('name'); 50 | $this->forge->addKey('uid'); 51 | $this->forge->addKey(['deleted_at', 'id']); 52 | $this->forge->addKey('created_at'); 53 | 54 | $this->forge->createTable('factories'); 55 | } 56 | 57 | public function down() 58 | { 59 | $this->forge->dropTable('factories'); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CodeIgniter 4 Application Starter 2 | 3 | ## What is CodeIgniter? 4 | 5 | CodeIgniter is a PHP full-stack web framework that is light, fast, flexible, and secure. 6 | More information can be found at the [official site](http://codeigniter.com). 7 | 8 | This repository holds a composer-installable app starter. 9 | It has been built from the 10 | [development repository](https://github.com/codeigniter4/CodeIgniter4). 11 | 12 | More information about the plans for version 4 can be found in [the announcement](http://forum.codeigniter.com/thread-62615.html) on the forums. 13 | 14 | The user guide corresponding to this version of the framework can be found 15 | [here](https://codeigniter4.github.io/userguide/). 16 | 17 | ## Setup 18 | Copy `env` to `.env` and tailor for your app, specifically the baseURL 19 | and any database settings. 20 | import the database starter.sql to your web database server and run the application with local development server 'php spark serve', Username : user Email : user@gmail.com Password : starter12345 21 | 22 | # Features 23 | CRUD Query Builder with Modal Bootstrap, 24 | CRUD Object Model, 25 | CRUD Ajax jQuery, 26 | Export to PDF with TCPDF, 27 | Export to XLSX with Phpspreadsheet, 28 | Login system with Myth/Auth, 29 | SweetAlert2, 30 | Clean code, 31 | Protection from (SQL injection, CSRF and XSS) 32 | 33 | # Tutorial CodeIgniter 4 34 | Please visit my channel to see exciting CodeIgniter 4 tutorials : [Muba Teknologi Youtube](https://youtube.com/@mubateknologi) 35 | 36 | -------------------------------------------------------------------------------- /app/Config/Modules.php: -------------------------------------------------------------------------------- 1 | session = \Config\Services::session(); 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /public/index.php: -------------------------------------------------------------------------------- 1 | systemDirectory, '/ ') . '/bootstrap.php'; 37 | 38 | /* 39 | *--------------------------------------------------------------- 40 | * LAUNCH THE APPLICATION 41 | *--------------------------------------------------------------- 42 | * Now that everything is setup, it's time to actually fire 43 | * up the engines and make this app do its thang. 44 | */ 45 | $app->run(); 46 | -------------------------------------------------------------------------------- /app/Config/Migrations.php: -------------------------------------------------------------------------------- 1 | php spark migrate:create 41 | | 42 | | Typical formats: 43 | | YmdHis_ 44 | | Y-m-d-His_ 45 | | Y_m_d_His_ 46 | | 47 | */ 48 | public $timestampFormat = 'Y-m-d-His_'; 49 | 50 | } 51 | -------------------------------------------------------------------------------- /app/Config/Events.php: -------------------------------------------------------------------------------- 1 | 0) 32 | { 33 | ob_end_flush(); 34 | } 35 | 36 | ob_start(function ($buffer) { 37 | return $buffer; 38 | }); 39 | } 40 | 41 | /* 42 | * -------------------------------------------------------------------- 43 | * Debug Toolbar Listeners. 44 | * -------------------------------------------------------------------- 45 | * If you delete, they will no longer be collected. 46 | */ 47 | if (ENVIRONMENT !== 'production') 48 | { 49 | Events::on('DBQuery', 'CodeIgniter\Debug\Toolbar\Collectors\Database::collect'); 50 | Services::toolbar()->respond(); 51 | } 52 | }); 53 | -------------------------------------------------------------------------------- /app/Config/Kint.php: -------------------------------------------------------------------------------- 1 | Secrets 46 | # env: 47 | # https://getcomposer.org/doc/articles/troubleshooting.md#api-rate-limit-and-oauth-tokens 48 | # COMPOSER_AUTH: ${{ secrets.COMPOSER_AUTH }} 49 | 50 | - name: Test with phpunit 51 | run: vendor/bin/phpunit --coverage-text 52 | -------------------------------------------------------------------------------- /app/Config/ContentSecurityPolicy.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 404 Page Not Found 6 | 7 | 70 | 71 | 72 |
73 |

404 - File Not Found

74 | 75 |

76 | 77 | 78 | 79 | Sorry! Cannot seem to find the page you were looking for. 80 | 81 |

82 |
83 | 84 | 85 | -------------------------------------------------------------------------------- /public/.htaccess: -------------------------------------------------------------------------------- 1 | # Disable directory browsing 2 | Options All -Indexes 3 | 4 | # ---------------------------------------------------------------------- 5 | # Rewrite engine 6 | # ---------------------------------------------------------------------- 7 | 8 | # Turning on the rewrite engine is necessary for the following rules and features. 9 | # FollowSymLinks must be enabled for this to work. 10 | 11 | Options +FollowSymlinks 12 | RewriteEngine On 13 | 14 | # If you installed CodeIgniter in a subfolder, you will need to 15 | # change the following line to match the subfolder you need. 16 | # http://httpd.apache.org/docs/current/mod/mod_rewrite.html#rewritebase 17 | # RewriteBase / 18 | 19 | # Redirect Trailing Slashes... 20 | RewriteCond %{REQUEST_FILENAME} !-d 21 | RewriteRule ^(.*)/$ /$1 [L,R=301] 22 | 23 | # Rewrite "www.example.com -> example.com" 24 | RewriteCond %{HTTPS} !=on 25 | RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC] 26 | RewriteRule ^ http://%1%{REQUEST_URI} [R=301,L] 27 | 28 | # Checks to see if the user is attempting to access a valid file, 29 | # such as an image or css document, if this isn't true it sends the 30 | # request to the front controller, index.php 31 | RewriteCond %{REQUEST_FILENAME} !-f 32 | RewriteCond %{REQUEST_FILENAME} !-d 33 | RewriteRule ^([\s\S]*)$ index.php/$1 [L,NC,QSA] 34 | 35 | # Ensure Authorization header is passed along 36 | RewriteCond %{HTTP:Authorization} . 37 | RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}] 38 | 39 | 40 | 41 | # If we don't have mod_rewrite installed, all 404's 42 | # can be sent to index.php, and everything works as normal. 43 | ErrorDocument 404 index.php 44 | 45 | 46 | # Disable server signature start 47 | ServerSignature Off 48 | # Disable server signature end 49 | -------------------------------------------------------------------------------- /app/Views/export/pdf.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | Print PDF 13 | 14 | 15 | 16 | 17 |
18 |

List of Peoples

19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 |
NoNameAddressEmailBirthdate
38 |
39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /spark: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | systemDirectory, '/ ') . '/bootstrap.php'; 45 | 46 | // Grab our Console 47 | $console = new \CodeIgniter\CLI\Console($app); 48 | 49 | // We want errors to be shown when using it from the CLI. 50 | error_reporting(-1); 51 | ini_set('display_errors', 1); 52 | 53 | // Show basic information before we do anything else. 54 | $console->showHeader(); 55 | 56 | // fire off the command in the main framework. 57 | $response = $console->run(); 58 | if ($response->getStatusCode() >= 300) 59 | { 60 | exit($response->getStatusCode()); 61 | } 62 | -------------------------------------------------------------------------------- /tests/_support/Libraries/ConfigReader.php: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | 14 | ./tests 15 | 16 | 17 | 18 | 19 | 20 | ./app 21 | 22 | ./app/Views 23 | ./app/Config/Routes.php 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /app/Config/Autoload.php: -------------------------------------------------------------------------------- 1 | SYSTEMPATH, 37 | * 'App' => APPPATH 38 | * ]; 39 | * 40 | * @var array 41 | */ 42 | public $psr4 = [ 43 | APP_NAMESPACE => APPPATH, // For custom app namespace 44 | 'Config' => APPPATH . 'Config', 45 | ]; 46 | 47 | /** 48 | * ------------------------------------------------------------------- 49 | * Class Map 50 | * ------------------------------------------------------------------- 51 | * The class map provides a map of class names and their exact 52 | * location on the drive. Classes loaded in this manner will have 53 | * slightly faster performance because they will not have to be 54 | * searched for within one or more directories as they would if they 55 | * were being autoloaded through a namespace. 56 | * 57 | * Prototype: 58 | * 59 | * $classmap = [ 60 | * 'MyClass' => '/path/to/class/file.php' 61 | * ]; 62 | * 63 | * @var array 64 | */ 65 | public $classmap = [ 66 | // 'TCPDF' => APPPATH .'vendor/autoload.php', //library mPDF 67 | ]; 68 | } 69 | -------------------------------------------------------------------------------- /app/Config/Routes.php: -------------------------------------------------------------------------------- 1 | setDefaultNamespace('App\Controllers'); 20 | $routes->setDefaultController('Home'); 21 | $routes->setDefaultMethod('index'); 22 | $routes->setTranslateURIDashes(false); 23 | $routes->set404Override(); 24 | $routes->setAutoRoute(true); 25 | 26 | /** 27 | * -------------------------------------------------------------------- 28 | * Route Definitions 29 | * -------------------------------------------------------------------- 30 | */ 31 | 32 | // We get a performance increase by specifying the default 33 | // route since we don't have to scan directories. 34 | $routes->get('/', 'Dashboard::index'); 35 | $routes->get('/export-pdf', 'Export::export_pdf'); 36 | $routes->get('/export-excel', 'Export::export_excel'); 37 | 38 | $routes->get('/ajax-jquery', 'Ajax::index'); 39 | $routes->get('/ajax-jquery/get_data', 'Ajax::get_data'); 40 | $routes->get('/ajax-jquery/get_modal', 'Ajax::get_modal'); 41 | $routes->post('/ajax-jquery/save_data', 'Ajax::save_data'); 42 | $routes->post('/ajax-jquery/get_modal_edit', 'Ajax::get_modal_edit'); 43 | $routes->post('/ajax-jquery/update_data', 'Ajax::update_data'); 44 | $routes->post('/ajax-jquery/delete_data', 'Ajax::delete_data'); 45 | 46 | /** 47 | * -------------------------------------------------------------------- 48 | * Additional Routing 49 | * -------------------------------------------------------------------- 50 | * 51 | * There will often be times that you need additional routing and you 52 | * need it to be able to override any defaults in this file. Environment 53 | * based routes is one such time. require() additional route files here 54 | * to make that happen. 55 | * 56 | * You will have access to the $routes object within that file without 57 | * needing to reload it. 58 | */ 59 | if (file_exists(APPPATH . 'Config/' . ENVIRONMENT . '/Routes.php')) { 60 | require APPPATH . 'Config/' . ENVIRONMENT . '/Routes.php'; 61 | } 62 | -------------------------------------------------------------------------------- /app/Views/layout/sidebar.php: -------------------------------------------------------------------------------- 1 |
2 |
3 | 45 |
-------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | #------------------------- 2 | # Operating Specific Junk Files 3 | #------------------------- 4 | 5 | # OS X 6 | .DS_Store 7 | .AppleDouble 8 | .LSOverride 9 | 10 | # OS X Thumbnails 11 | ._* 12 | 13 | # Windows image file caches 14 | Thumbs.db 15 | ehthumbs.db 16 | Desktop.ini 17 | 18 | # Recycle Bin used on file shares 19 | $RECYCLE.BIN/ 20 | 21 | # Windows Installer files 22 | *.cab 23 | *.msi 24 | *.msm 25 | *.msp 26 | 27 | # Windows shortcuts 28 | *.lnk 29 | 30 | # Linux 31 | *~ 32 | 33 | # KDE directory preferences 34 | .directory 35 | 36 | # Linux trash folder which might appear on any partition or disk 37 | .Trash-* 38 | 39 | #------------------------- 40 | # Environment Files 41 | #------------------------- 42 | # These should never be under version control, 43 | # as it poses a security risk. 44 | .env 45 | .vagrant 46 | Vagrantfile 47 | 48 | #------------------------- 49 | # Temporary Files 50 | #------------------------- 51 | writable/cache/* 52 | !writable/cache/index.html 53 | 54 | writable/logs/* 55 | !writable/logs/index.html 56 | 57 | writable/session/* 58 | !writable/session/index.html 59 | 60 | writable/uploads/* 61 | !writable/uploads/index.html 62 | 63 | writable/debugbar/* 64 | 65 | php_errors.log 66 | 67 | #------------------------- 68 | # User Guide Temp Files 69 | #------------------------- 70 | user_guide_src/build/* 71 | user_guide_src/cilexer/build/* 72 | user_guide_src/cilexer/dist/* 73 | user_guide_src/cilexer/pycilexer.egg-info/* 74 | 75 | #------------------------- 76 | # Test Files 77 | #------------------------- 78 | tests/coverage* 79 | 80 | # Don't save phpunit under version control. 81 | phpunit 82 | 83 | #------------------------- 84 | # Composer 85 | #------------------------- 86 | vendor/ 87 | 88 | #------------------------- 89 | # IDE / Development Files 90 | #------------------------- 91 | 92 | # Modules Testing 93 | _modules/* 94 | 95 | # phpenv local config 96 | .php-version 97 | 98 | # Jetbrains editors (PHPStorm, etc) 99 | .idea/ 100 | *.iml 101 | 102 | # Netbeans 103 | nbproject/ 104 | build/ 105 | nbbuild/ 106 | dist/ 107 | nbdist/ 108 | nbactions.xml 109 | nb-configuration.xml 110 | .nb-gradle/ 111 | 112 | # Sublime Text 113 | *.tmlanguage.cache 114 | *.tmPreferences.cache 115 | *.stTheme.cache 116 | *.sublime-workspace 117 | *.sublime-project 118 | .phpintel 119 | /api/ 120 | 121 | # Visual Studio Code 122 | .vscode/ 123 | 124 | /results/ 125 | /phpunit*.xml 126 | /.phpunit.*.cache 127 | 128 | -------------------------------------------------------------------------------- /app/Config/DocTypes.php: -------------------------------------------------------------------------------- 1 | '', 14 | 'xhtml1-strict' => '', 15 | 'xhtml1-trans' => '', 16 | 'xhtml1-frame' => '', 17 | 'xhtml-basic11' => '', 18 | 'html5' => '', 19 | 'html4-strict' => '', 20 | 'html4-trans' => '', 21 | 'html4-frame' => '', 22 | 'mathml1' => '', 23 | 'mathml2' => '', 24 | 'svg10' => '', 25 | 'svg11' => '', 26 | 'svg11-basic' => '', 27 | 'svg11-tiny' => '', 28 | 'xhtml-math-svg-xh' => '', 29 | 'xhtml-math-svg-sh' => '', 30 | 'xhtml-rdfa-1' => '', 31 | 'xhtml-rdfa-2' => '', 32 | ]; 33 | } 34 | -------------------------------------------------------------------------------- /app/Views/ajax/index.php: -------------------------------------------------------------------------------- 1 | extend('layout/templates'); ?> 2 | 3 | Section('content'); ?> 4 |
5 |
6 |
7 |

CRUD Ajax jQuery

8 | 12 |
13 |
14 | 15 | Products Table 16 |
17 |
18 | Add data 19 | 20 | 21 |
22 | 23 |
24 |
25 |
26 |
27 |
28 | 29 | 30 | 31 | 32 | 68 | 69 | endSection(); ?> -------------------------------------------------------------------------------- /app/Controllers/Builder.php: -------------------------------------------------------------------------------- 1 | builderModel = new BuilderModel(); 14 | helper('form'); 15 | } 16 | 17 | public function index() 18 | { 19 | $data = [ 20 | 'title' => 'Crud Query Builder', 21 | 'all_data' => $this->builderModel->select_data() // selecting all data 22 | ]; 23 | 24 | return view('builder/index', $data); 25 | } 26 | 27 | public function add_data() 28 | { 29 | // validation 30 | $rules = $this->validate([ 31 | 'name' => 'required', 32 | 'department' => 'required', 33 | 'position' => 'required', 34 | 'age' => 'required|numeric' 35 | ]); 36 | 37 | if (!$rules) { 38 | session()->setFlashData('failed', \Config\Services::validation()->getErrors()); 39 | return redirect()->back(); 40 | } 41 | 42 | $data = [ 43 | 'name' => $this->request->getPost('name'), 44 | 'department' => $this->request->getPost('department'), 45 | 'position' => $this->request->getPost('position'), 46 | 'age' => $this->request->getPost('age') 47 | ]; 48 | 49 | $this->builderModel->add_data($data); 50 | session()->setFlashData('success', 'data has been added to database.'); 51 | return redirect()->back(); 52 | } 53 | 54 | public function delete_data($id) 55 | { 56 | $this->builderModel->delete_data($id); 57 | session()->setFlashData('success', 'data has been deleted from database.'); 58 | return redirect()->back(); 59 | } 60 | 61 | public function update_data($id) 62 | { 63 | // validation 64 | $rules = $this->validate([ 65 | 'name' => 'required', 66 | 'department' => 'required', 67 | 'position' => 'required', 68 | 'age' => 'required|numeric' 69 | ]); 70 | 71 | if (!$rules) { 72 | session()->setFlashData('failed', \Config\Services::validation()->getErrors()); 73 | return redirect()->back(); 74 | } 75 | 76 | $data = [ 77 | 'name' => $this->request->getPost('name'), 78 | 'department' => $this->request->getPost('department'), 79 | 'position' => $this->request->getPost('position'), 80 | 'age' => $this->request->getPost('age') 81 | ]; 82 | 83 | $this->builderModel->update_data($id, $data); 84 | session()->setFlashData('success', 'data has been updated from database.'); 85 | return redirect()->back(); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /app/Config/Toolbar.php: -------------------------------------------------------------------------------- 1 | \CodeIgniter\Format\JSONFormatter::class, 39 | 'application/xml' => \CodeIgniter\Format\XMLFormatter::class, 40 | 'text/xml' => \CodeIgniter\Format\XMLFormatter::class, 41 | ]; 42 | 43 | /* 44 | |-------------------------------------------------------------------------- 45 | | Formatters Options 46 | |-------------------------------------------------------------------------- 47 | | 48 | | Additional Options to adjust default formatters behaviour. 49 | | For each mime type, list the additional options that should be used. 50 | | 51 | */ 52 | public $formatterOptions = [ 53 | 'application/json' => JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES, 54 | 'application/xml' => 0, 55 | 'text/xml' => 0, 56 | ]; 57 | //-------------------------------------------------------------------- 58 | 59 | /** 60 | * A Factory method to return the appropriate formatter for the given mime type. 61 | * 62 | * @param string $mime 63 | * 64 | * @return \CodeIgniter\Format\FormatterInterface 65 | */ 66 | public function getFormatter(string $mime) 67 | { 68 | if (! array_key_exists($mime, $this->formatters)) 69 | { 70 | throw new \InvalidArgumentException('No Formatter defined for mime type: ' . $mime); 71 | } 72 | 73 | $class = $this->formatters[$mime]; 74 | 75 | if (! class_exists($class)) 76 | { 77 | throw new \BadMethodCallException($class . ' is not a valid Formatter.'); 78 | } 79 | 80 | return new $class(); 81 | } 82 | 83 | //-------------------------------------------------------------------- 84 | 85 | } 86 | -------------------------------------------------------------------------------- /app/Controllers/Export.php: -------------------------------------------------------------------------------- 1 | db = \Config\Database::connect(); 16 | $this->builder = $this->db->table('peoples'); 17 | } 18 | 19 | public function export_pdf() 20 | { 21 | $data = [ 22 | 'pdf_peoples' => $this->builder->get()->getResultArray() 23 | ]; 24 | 25 | $html = view('export/pdf', $data); 26 | 27 | // create new PDF document 28 | $pdf = new TCPDF(PDF_PAGE_ORIENTATION, PDF_UNIT, PDF_PAGE_FORMAT, true, 'UTF-8', false); 29 | 30 | 31 | // set margins 32 | $pdf->SetMargins(PDF_MARGIN_LEFT, PDF_MARGIN_RIGHT); 33 | $pdf->SetFooterMargin(PDF_MARGIN_FOOTER); 34 | 35 | // Add a page 36 | // This method has several options, check the source code documentation for more information. 37 | $pdf->AddPage(); 38 | 39 | // Print text using writeHTMLCell() 40 | $pdf->writeHTML($html); 41 | $this->response->setContentType('application/pdf'); 42 | // Close and output PDF document 43 | // This method has several options, check the source code documentation for more information. 44 | $pdf->Output('peoples.pdf', 'I'); 45 | } 46 | 47 | public function export_excel() 48 | { 49 | $peoples = $this->builder->get()->getResultArray(); 50 | 51 | $spreadsheet = new Spreadsheet(); 52 | // tulis header/nama kolom 53 | $spreadsheet->setActiveSheetIndex(0) 54 | ->setCellValue('A1', 'No') 55 | ->setCellValue('B1', 'Name') 56 | ->setCellValue('C1', 'Address') 57 | ->setCellValue('D1', 'Email') 58 | ->setCellValue('E1', 'Birthdate'); 59 | 60 | $column = 2; 61 | // tulis data mobil ke cell 62 | $no = 1; 63 | foreach ($peoples as $data) { 64 | $spreadsheet->setActiveSheetIndex(0) 65 | ->setCellValue('A' . $column, $no++) 66 | ->setCellValue('B' . $column, $data['name']) 67 | ->setCellValue('C' . $column, $data['address']) 68 | ->setCellValue('D' . $column, $data['email']) 69 | ->setCellValue('E' . $column, $data['birthdate']); 70 | $column++; 71 | } 72 | // tulis dalam format .xlsx 73 | $writer = new Xlsx($spreadsheet); 74 | $fileName = 'Peoples Data'; 75 | 76 | // Redirect hasil generate xlsx ke web client 77 | header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'); 78 | header('Content-Disposition: attachment;filename=' . $fileName . '.xlsx'); 79 | header('Cache-Control: max-age=0'); 80 | 81 | $writer->save('php://output'); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /app/Config/Paths.php: -------------------------------------------------------------------------------- 1 | '', 34 | 'hostname' => 'localhost', 35 | 'username' => '', 36 | 'password' => '', 37 | 'database' => '', 38 | 'DBDriver' => 'MySQLi', 39 | 'DBPrefix' => '', 40 | 'pConnect' => false, 41 | 'DBDebug' => (ENVIRONMENT !== 'production'), 42 | 'cacheOn' => false, 43 | 'cacheDir' => '', 44 | 'charset' => 'utf8', 45 | 'DBCollat' => 'utf8_general_ci', 46 | 'swapPre' => '', 47 | 'encrypt' => false, 48 | 'compress' => false, 49 | 'strictOn' => false, 50 | 'failover' => [], 51 | 'port' => 3306, 52 | ]; 53 | 54 | /** 55 | * This database connection is used when 56 | * running PHPUnit database tests. 57 | * 58 | * @var array 59 | */ 60 | public $tests = [ 61 | 'DSN' => '', 62 | 'hostname' => '127.0.0.1', 63 | 'username' => '', 64 | 'password' => '', 65 | 'database' => ':memory:', 66 | 'DBDriver' => 'SQLite3', 67 | 'DBPrefix' => 'db_', // Needed to ensure we're working correctly with prefixes live. DO NOT REMOVE FOR CI DEVS 68 | 'pConnect' => false, 69 | 'DBDebug' => (ENVIRONMENT !== 'production'), 70 | 'cacheOn' => false, 71 | 'cacheDir' => '', 72 | 'charset' => 'utf8', 73 | 'DBCollat' => 'utf8_general_ci', 74 | 'swapPre' => '', 75 | 'encrypt' => false, 76 | 'compress' => false, 77 | 'strictOn' => false, 78 | 'failover' => [], 79 | 'port' => 3306, 80 | ]; 81 | 82 | //-------------------------------------------------------------------- 83 | 84 | public function __construct() 85 | { 86 | parent::__construct(); 87 | 88 | // Ensure that we always set the database group to 'tests' if 89 | // we are currently running an automated test suite, so that 90 | // we don't overwrite live data on accident. 91 | if (ENVIRONMENT === 'testing') 92 | { 93 | $this->defaultGroup = 'tests'; 94 | 95 | // Under Travis-CI, we can set an ENV var named 'DB_GROUP' 96 | // so that we can test against multiple databases. 97 | if ($group = getenv('DB')) 98 | { 99 | if (is_file(TESTPATH . 'travis/Database.php')) 100 | { 101 | require TESTPATH . 'travis/Database.php'; 102 | 103 | if (! empty($dbconfig) && array_key_exists($group, $dbconfig)) 104 | { 105 | $this->tests = $dbconfig[$group]; 106 | } 107 | } 108 | } 109 | } 110 | } 111 | 112 | //-------------------------------------------------------------------- 113 | 114 | } 115 | -------------------------------------------------------------------------------- /app/Config/Email.php: -------------------------------------------------------------------------------- 1 | extend('layout/templates'); ?> 2 | 3 | Section('content'); ?> 4 | 5 |
6 |
7 |
8 |

Dashboard

9 | 12 |
13 |
14 |
15 |
Primary Card
16 | 20 |
21 |
22 |
23 |
24 |
Warning Card
25 | 29 |
30 |
31 |
32 |
33 |
Success Card
34 | 38 |
39 |
40 |
41 |
42 |
Danger Card
43 | 47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 | 55 | Area Chart Example 56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 | 64 | Bar Chart Example 65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 | 73 | endSection(); ?> -------------------------------------------------------------------------------- /app/Views/ajax/view_data.php: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 31 | 32 | 33 | 34 |
NoNameDescriptionPriceDateOption
23 | 26 | 27 | 30 |
35 |
36 | 37 | -------------------------------------------------------------------------------- /app/Views/errors/html/debug.js: -------------------------------------------------------------------------------- 1 | //-------------------------------------------------------------------- 2 | // Tabs 3 | //-------------------------------------------------------------------- 4 | 5 | var tabLinks = new Array(); 6 | var contentDivs = new Array(); 7 | 8 | function init () 9 | { 10 | 11 | // Grab the tab links and content divs from the page 12 | var tabListItems = document.getElementById('tabs').childNodes; 13 | console.log(tabListItems); 14 | for (var i = 0; i < tabListItems.length; i ++) 15 | { 16 | if (tabListItems[i].nodeName == "LI") 17 | { 18 | var tabLink = getFirstChildWithTagName(tabListItems[i], 'A'); 19 | var id = getHash(tabLink.getAttribute('href')); 20 | tabLinks[id] = tabLink; 21 | contentDivs[id] = document.getElementById(id); 22 | } 23 | } 24 | 25 | // Assign onclick events to the tab links, and 26 | // highlight the first tab 27 | var i = 0; 28 | 29 | for (var id in tabLinks) 30 | { 31 | tabLinks[id].onclick = showTab; 32 | tabLinks[id].onfocus = function () { this.blur() }; 33 | if (i == 0) 34 | { 35 | tabLinks[id].className = 'active'; 36 | } 37 | i ++; 38 | } 39 | 40 | // Hide all content divs except the first 41 | var i = 0; 42 | 43 | for (var id in contentDivs) 44 | { 45 | if (i != 0) 46 | { 47 | console.log(contentDivs[id]); 48 | contentDivs[id].className = 'content hide'; 49 | } 50 | i ++; 51 | } 52 | } 53 | 54 | //-------------------------------------------------------------------- 55 | 56 | function showTab () 57 | { 58 | var selectedId = getHash(this.getAttribute('href')); 59 | 60 | // Highlight the selected tab, and dim all others. 61 | // Also show the selected content div, and hide all others. 62 | for (var id in contentDivs) 63 | { 64 | if (id == selectedId) 65 | { 66 | tabLinks[id].className = 'active'; 67 | contentDivs[id].className = 'content'; 68 | } 69 | else 70 | { 71 | tabLinks[id].className = ''; 72 | contentDivs[id].className = 'content hide'; 73 | } 74 | } 75 | 76 | // Stop the browser following the link 77 | return false; 78 | } 79 | 80 | //-------------------------------------------------------------------- 81 | 82 | function getFirstChildWithTagName (element, tagName) 83 | { 84 | for (var i = 0; i < element.childNodes.length; i ++) 85 | { 86 | if (element.childNodes[i].nodeName == tagName) 87 | { 88 | return element.childNodes[i]; 89 | } 90 | } 91 | } 92 | 93 | //-------------------------------------------------------------------- 94 | 95 | function getHash (url) 96 | { 97 | var hashPos = url.lastIndexOf('#'); 98 | return url.substring(hashPos + 1); 99 | } 100 | 101 | //-------------------------------------------------------------------- 102 | 103 | function toggle (elem) 104 | { 105 | elem = document.getElementById(elem); 106 | 107 | if (elem.style && elem.style['display']) 108 | { 109 | // Only works with the "style" attr 110 | var disp = elem.style['display']; 111 | } 112 | else if (elem.currentStyle) 113 | { 114 | // For MSIE, naturally 115 | var disp = elem.currentStyle['display']; 116 | } 117 | else if (window.getComputedStyle) 118 | { 119 | // For most other browsers 120 | var disp = document.defaultView.getComputedStyle(elem, null).getPropertyValue('display'); 121 | } 122 | 123 | // Toggle the state of the "display" style 124 | elem.style.display = disp == 'block' ? 'none' : 'block'; 125 | 126 | return false; 127 | } -------------------------------------------------------------------------------- /env: -------------------------------------------------------------------------------- 1 | #-------------------------------------------------------------------- 2 | # Example Environment Configuration file 3 | # 4 | # This file can be used as a starting point for your own 5 | # custom .env files, and contains most of the possible settings 6 | # available in a default install. 7 | # 8 | # By default, all of the settings are commented out. If you want 9 | # to override the setting, you must un-comment it by removing the '#' 10 | # at the beginning of the line. 11 | #-------------------------------------------------------------------- 12 | 13 | #-------------------------------------------------------------------- 14 | # ENVIRONMENT 15 | #-------------------------------------------------------------------- 16 | 17 | # CI_ENVIRONMENT = production 18 | 19 | #-------------------------------------------------------------------- 20 | # APP 21 | #-------------------------------------------------------------------- 22 | 23 | # app.baseURL = '' 24 | # app.forceGlobalSecureRequests = false 25 | 26 | # app.sessionDriver = 'CodeIgniter\Session\Handlers\FileHandler' 27 | # app.sessionCookieName = 'ci_session' 28 | # app.sessionSavePath = NULL 29 | # app.sessionMatchIP = false 30 | # app.sessionTimeToUpdate = 300 31 | # app.sessionRegenerateDestroy = false 32 | 33 | # app.cookiePrefix = '' 34 | # app.cookieDomain = '' 35 | # app.cookiePath = '/' 36 | # app.cookieSecure = false 37 | # app.cookieHTTPOnly = false 38 | 39 | # app.CSRFProtection = false 40 | # app.CSRFTokenName = 'csrf_test_name' 41 | # app.CSRFCookieName = 'csrf_cookie_name' 42 | # app.CSRFExpire = 7200 43 | # app.CSRFRegenerate = true 44 | # app.CSRFExcludeURIs = [] 45 | 46 | # app.CSPEnabled = false 47 | 48 | #-------------------------------------------------------------------- 49 | # DATABASE 50 | #-------------------------------------------------------------------- 51 | 52 | # database.default.hostname = localhost 53 | # database.default.database = ci4 54 | # database.default.username = root 55 | # database.default.password = root 56 | # database.default.DBDriver = MySQLi 57 | 58 | # database.tests.hostname = localhost 59 | # database.tests.database = ci4 60 | # database.tests.username = root 61 | # database.tests.password = root 62 | # database.tests.DBDriver = MySQLi 63 | 64 | #-------------------------------------------------------------------- 65 | # CONTENT SECURITY POLICY 66 | #-------------------------------------------------------------------- 67 | 68 | # contentsecuritypolicy.reportOnly = false 69 | # contentsecuritypolicy.defaultSrc = 'none' 70 | # contentsecuritypolicy.scriptSrc = 'self' 71 | # contentsecuritypolicy.styleSrc = 'self' 72 | # contentsecuritypolicy.imageSrc = 'self' 73 | # contentsecuritypolicy.base_uri = null 74 | # contentsecuritypolicy.childSrc = null 75 | # contentsecuritypolicy.connectSrc = 'self' 76 | # contentsecuritypolicy.fontSrc = null 77 | # contentsecuritypolicy.formAction = null 78 | # contentsecuritypolicy.frameAncestors = null 79 | # contentsecuritypolicy.mediaSrc = null 80 | # contentsecuritypolicy.objectSrc = null 81 | # contentsecuritypolicy.pluginTypes = null 82 | # contentsecuritypolicy.reportURI = null 83 | # contentsecuritypolicy.sandbox = false 84 | # contentsecuritypolicy.upgradeInsecureRequests = false 85 | 86 | #-------------------------------------------------------------------- 87 | # ENCRYPTION 88 | #-------------------------------------------------------------------- 89 | 90 | # encryption.key = 91 | # encryption.driver = OpenSSL 92 | 93 | #-------------------------------------------------------------------- 94 | # HONEYPOT 95 | #-------------------------------------------------------------------- 96 | 97 | # honeypot.hidden = 'true' 98 | # honeypot.label = 'Fill This Field' 99 | # honeypot.name = 'honeypot' 100 | # honeypot.template = '' 101 | # honeypot.container = '
{template}
' 102 | -------------------------------------------------------------------------------- /app/Config/Cache.php: -------------------------------------------------------------------------------- 1 | '127.0.0.1', 82 | 'port' => 11211, 83 | 'weight' => 1, 84 | 'raw' => false, 85 | ]; 86 | 87 | /* 88 | | ------------------------------------------------------------------------- 89 | | Redis settings 90 | | ------------------------------------------------------------------------- 91 | | Your Redis server can be specified below, if you are using 92 | | the Redis or Predis drivers. 93 | | 94 | */ 95 | public $redis = [ 96 | 'host' => '127.0.0.1', 97 | 'password' => null, 98 | 'port' => 6379, 99 | 'timeout' => 0, 100 | 'database' => 0, 101 | ]; 102 | 103 | /* 104 | |-------------------------------------------------------------------------- 105 | | Available Cache Handlers 106 | |-------------------------------------------------------------------------- 107 | | 108 | | This is an array of cache engine alias' and class names. Only engines 109 | | that are listed here are allowed to be used. 110 | | 111 | */ 112 | public $validHandlers = [ 113 | 'dummy' => \CodeIgniter\Cache\Handlers\DummyHandler::class, 114 | 'file' => \CodeIgniter\Cache\Handlers\FileHandler::class, 115 | 'memcached' => \CodeIgniter\Cache\Handlers\MemcachedHandler::class, 116 | 'predis' => \CodeIgniter\Cache\Handlers\PredisHandler::class, 117 | 'redis' => \CodeIgniter\Cache\Handlers\RedisHandler::class, 118 | 'wincache' => \CodeIgniter\Cache\Handlers\WincacheHandler::class, 119 | ]; 120 | } 121 | -------------------------------------------------------------------------------- /app/Controllers/Objects.php: -------------------------------------------------------------------------------- 1 | objectModel = new ObjectModel(); 14 | helper('form'); 15 | } 16 | 17 | public function index() 18 | { 19 | $data = [ 20 | 'title' => 'CRUD Object Model', 21 | 'all_data' => $this->objectModel->findAll() 22 | ]; 23 | 24 | return view('objects/index', $data); 25 | } 26 | 27 | public function add_data() 28 | { 29 | $data['title'] = 'Add Data'; 30 | if ($this->request->getPost()) { 31 | $rules = [ 32 | 'name' => 'required|alpha_space', 33 | 'gender' => 'required', 34 | 'address' => 'required', 35 | 'photo' => 'uploaded[photo]|max_size[photo,2048]|is_image[photo]|mime_in[photo,image/jpg,image/jpeg,image/png]' 36 | ]; 37 | 38 | if ($this->validate($rules)) { 39 | $photo = $this->request->getFile('photo'); 40 | $photoName = $photo->getRandomName(); 41 | $photo->move('photos', $photoName); 42 | 43 | $inserted = [ 44 | 'name' => $this->request->getPost('name'), 45 | 'gender' => $this->request->getPost('gender'), 46 | 'address' => $this->request->getPost('address'), 47 | 'photo' => $photoName 48 | ]; 49 | 50 | $this->objectModel->insert($inserted); 51 | session()->setFlashData('success', 'data has been added to database'); 52 | return redirect()->to('/objects'); 53 | } else { 54 | session()->setFlashData('failed', \Config\Services::validation()->getErrors()); 55 | return redirect()->back()->withInput(); 56 | } 57 | } 58 | return view('objects/form_add', $data); 59 | } 60 | 61 | public function delete_data($id) 62 | { 63 | $photoId = $this->objectModel->find($id); 64 | unlink('photos/'.$photoId['photo']); 65 | 66 | $this->objectModel->delete($id); 67 | session()->setFlashData('success', 'data has been deleted from database.'); 68 | return redirect()->to('/objects'); 69 | } 70 | 71 | public function update_data($id) 72 | { 73 | $data = [ 74 | 'title' => 'Update Data', 75 | 'dataById' => $this->objectModel->where('id', $id)->first() 76 | ]; 77 | 78 | if ($this->request->getPost()) { 79 | $rules = [ 80 | 'name' => 'required|alpha_space', 81 | 'gender' => 'required', 82 | 'address' => 'required', 83 | 'photo' => 'max_size[photo,2048]|is_image[photo]|mime_in[photo,image/jpg,image/jpeg,image/png]' 84 | ]; 85 | 86 | if ($this->validate($rules)) { 87 | $photo = $this->request->getFile('photo'); 88 | if ($photo->getError() == 4) { 89 | $photoName = $this->request->getPost('Oldphoto'); 90 | } else { 91 | $photoName = $photo->getRandomName(); 92 | $photo->move('photos', $photoName); 93 | $photo = $this->objectModel->find($id); 94 | if ($photo['photo'] == $photo['photo']) { 95 | unlink('photos/' . $this->request->getPost('Oldphoto')); 96 | } 97 | } 98 | 99 | $inserted = [ 100 | 'name' => $this->request->getPost('name'), 101 | 'gender' => $this->request->getPost('gender'), 102 | 'address' => $this->request->getPost('address'), 103 | 'photo' => $photoName 104 | ]; 105 | 106 | $this->objectModel->update($id, $inserted); 107 | session()->setFlashData('success', 'data has been updated from database'); 108 | return redirect()->to('/objects'); 109 | } else { 110 | session()->setFlashData('failed', \Config\Services::validation()->getErrors()); 111 | return redirect()->back()->withInput(); 112 | } 113 | } 114 | return view('objects/form_update', $data); 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /app/Views/objects/form_add.php: -------------------------------------------------------------------------------- 1 | extend('layout/templates'); ?> 2 | 3 | Section('content'); ?> 4 |
5 |
6 |
7 |

CRUD Object Model

8 | 13 |
14 |
15 | 16 | Form Add 17 |
18 |
19 | getFlashdata('failed'); 21 | if (!empty($errors)) : ?> 22 | 33 | 34 | 35 | 37 | 46 | 47 | 48 | 49 | 50 |
51 | 52 | 53 |
54 |
55 | 56 |
57 | 58 | 61 |
62 |
63 | 64 | 67 |
68 |
69 |
70 | 71 | 72 |
73 |
74 | 75 | 76 |
77 | 78 | Back 79 | 80 |
81 |
82 |
83 |
84 | endSection(); ?> -------------------------------------------------------------------------------- /app/Controllers/Ajax.php: -------------------------------------------------------------------------------- 1 | ajax = new AjaxModel(); 15 | } 16 | 17 | public function index() 18 | { 19 | $data = [ 20 | 'title' => 'CRUD Ajax jQuery' 21 | ]; 22 | 23 | return view('ajax/index', $data); 24 | } 25 | 26 | public function get_data() 27 | { 28 | if ($this->request->isAJAX()) { 29 | $data = [ 30 | 'data_products' => $this->ajax->findAll() 31 | ]; 32 | 33 | $result = [ 34 | 'output' => view('ajax/view_data', $data) 35 | ]; 36 | 37 | echo json_encode($result); 38 | } else { 39 | exit('404 Not Found'); 40 | } 41 | } 42 | 43 | public function get_modal() 44 | { 45 | if ($this->request->isAJAX()) { 46 | $result = [ 47 | 'output' => view('ajax/view_modal') 48 | ]; 49 | 50 | echo json_encode($result); 51 | } else { 52 | exit('404 Not Found'); 53 | } 54 | } 55 | 56 | public function save_data() 57 | { 58 | if ($this->request->isAJAX()) { 59 | $validation = \Config\Services::validation(); 60 | 61 | $rules = $this->validate([ 62 | 'name' => [ 63 | 'label' => 'product name', 64 | 'rules' => 'required|min_length[3]', 65 | ], 66 | 'description' => [ 67 | 'label' => 'product desc', 68 | 'rules' => 'required|min_length[3]', 69 | ], 70 | 'price' => [ 71 | 'label' => 'product price', 72 | 'rules' => 'required|numeric', 73 | ], 74 | 'date' => [ 75 | 'label' => 'product date', 76 | 'rules' => 'required', 77 | ] 78 | ]); 79 | 80 | if (!$rules) { 81 | $result = [ 82 | 'error' => [ 83 | 'name' => $validation->getError('name'), 84 | 'description' => $validation->getError('description'), 85 | 'price' => $validation->getError('price'), 86 | 'date' => $validation->getError('date') 87 | ] 88 | ]; 89 | } else { 90 | $this->ajax->insert([ 91 | 'name' => strip_tags($this->request->getPost('name')), 92 | 'description' => strip_tags($this->request->getPost('description')), 93 | 'price' => strip_tags($this->request->getPost('price')), 94 | 'date' => strip_tags($this->request->getPost('date')) 95 | ]); 96 | 97 | $result = [ 98 | 'success' => 'Data has been added to database' 99 | ]; 100 | } 101 | echo json_encode($result); 102 | } else { 103 | exit('404 Not Found'); 104 | } 105 | } 106 | 107 | public function get_modal_edit() 108 | { 109 | if ($this->request->isAJAX()) { 110 | $id = $this->request->getVar('id'); 111 | 112 | $row = $this->ajax->find($id); 113 | 114 | $data = [ 115 | 'id' => $row['id'], 116 | 'name' => $row['name'], 117 | 'description' => $row['description'], 118 | 'price' => $row['price'], 119 | 'date' => $row['date'] 120 | ]; 121 | 122 | $result = [ 123 | 'output' => view('ajax/view_modal_edit', $data) 124 | ]; 125 | 126 | echo json_encode($result); 127 | } else { 128 | exit('404 Not Found'); 129 | } 130 | } 131 | 132 | public function update_data() 133 | { 134 | if ($this->request->isAJAX()) { 135 | $validation = \Config\Services::validation(); 136 | 137 | $rules = $this->validate([ 138 | 'name' => [ 139 | 'label' => 'product name', 140 | 'rules' => 'required|min_length[3]', 141 | ], 142 | 'description' => [ 143 | 'label' => 'product desc', 144 | 'rules' => 'required|min_length[3]', 145 | ], 146 | 'price' => [ 147 | 'label' => 'product price', 148 | 'rules' => 'required|numeric', 149 | ], 150 | 'date' => [ 151 | 'label' => 'product date', 152 | 'rules' => 'required', 153 | ] 154 | ]); 155 | 156 | if (!$rules) { 157 | $result = [ 158 | 'error' => [ 159 | 'name' => $validation->getError('name'), 160 | 'description' => $validation->getError('description'), 161 | 'price' => $validation->getError('price'), 162 | 'date' => $validation->getError('date') 163 | ] 164 | ]; 165 | } else { 166 | $id = $this->request->getPost('id'); 167 | $this->ajax->update($id, [ 168 | 'name' => strip_tags($this->request->getPost('name')), 169 | 'description' => strip_tags($this->request->getPost('description')), 170 | 'price' => strip_tags($this->request->getPost('price')), 171 | 'date' => strip_tags($this->request->getPost('date')) 172 | ]); 173 | 174 | $result = [ 175 | 'success' => 'Data has been updated from database' 176 | ]; 177 | } 178 | echo json_encode($result); 179 | } else { 180 | exit('404 Not Found'); 181 | } 182 | } 183 | 184 | public function delete_data() 185 | { 186 | if ($this->request->isAJAX()) { 187 | $id = $this->request->getVar('id'); 188 | 189 | $this->ajax->delete($id); 190 | 191 | $result = [ 192 | 'output' => "Data has been deleted from database" 193 | ]; 194 | 195 | echo json_encode($result); 196 | } else { 197 | exit('404 Not Found'); 198 | } 199 | } 200 | } 201 | -------------------------------------------------------------------------------- /app/Config/Logger.php: -------------------------------------------------------------------------------- 1 | [ 79 | 80 | /* 81 | * The log levels that this handler will handle. 82 | */ 83 | 'handles' => [ 84 | 'critical', 85 | 'alert', 86 | 'emergency', 87 | 'debug', 88 | 'error', 89 | 'info', 90 | 'notice', 91 | 'warning', 92 | ], 93 | 94 | /* 95 | * The default filename extension for log files. 96 | * An extension of 'php' allows for protecting the log files via basic 97 | * scripting, when they are to be stored under a publicly accessible directory. 98 | * 99 | * Note: Leaving it blank will default to 'log'. 100 | */ 101 | 'fileExtension' => '', 102 | 103 | /* 104 | * The file system permissions to be applied on newly created log files. 105 | * 106 | * IMPORTANT: This MUST be an integer (no quotes) and you MUST use octal 107 | * integer notation (i.e. 0700, 0644, etc.) 108 | */ 109 | 'filePermissions' => 0644, 110 | 111 | /* 112 | * Logging Directory Path 113 | * 114 | * By default, logs are written to WRITEPATH . 'logs/' 115 | * Specify a different destination here, if desired. 116 | */ 117 | 'path' => '', 118 | ], 119 | 120 | /** 121 | * The ChromeLoggerHandler requires the use of the Chrome web browser 122 | * and the ChromeLogger extension. Uncomment this block to use it. 123 | */ 124 | // 'CodeIgniter\Log\Handlers\ChromeLoggerHandler' => [ 125 | // /* 126 | // * The log levels that this handler will handle. 127 | // */ 128 | // 'handles' => ['critical', 'alert', 'emergency', 'debug', 129 | // 'error', 'info', 'notice', 'warning'], 130 | // ] 131 | ]; 132 | } 133 | -------------------------------------------------------------------------------- /tests/README.md: -------------------------------------------------------------------------------- 1 | # Running Application Tests 2 | 3 | This is the quick-start to CodeIgniter testing. Its intent is to describe what 4 | it takes to set up your application and get it ready to run unit tests. 5 | It is not intended to be a full description of the test features that you can 6 | use to test your application. Those details can be found in the documentation. 7 | 8 | ## Resources 9 | * [CodeIgniter 4 User Guide on Testing](https://codeigniter4.github.io/userguide/testing/index.html) 10 | * [PHPUnit docs](https://phpunit.readthedocs.io/en/8.3/index.html) 11 | 12 | ## Requirements 13 | 14 | It is recommended to use the latest version of PHPUnit. At the time of this 15 | writing we are running version 8.5.2. Support for this has been built into the 16 | **composer.json** file that ships with CodeIgniter and can easily be installed 17 | via [Composer](https://getcomposer.org/) if you don't already have it installed globally. 18 | 19 | > composer install 20 | 21 | If running under OS X or Linux, you can create a symbolic link to make running tests a touch nicer. 22 | 23 | > ln -s ./vendor/bin/phpunit ./phpunit 24 | 25 | You also need to install [XDebug](https://xdebug.org/index.php) in order 26 | for code coverage to be calculated successfully. 27 | 28 | ## Setting Up 29 | 30 | A number of the tests use a running database. 31 | In order to set up the database edit the details for the `tests` group in 32 | **app/Config/Database.php** or **phpunit.xml**. Make sure that you provide a database engine 33 | that is currently running on your machine. More details on a test database setup are in the 34 | *Docs>>Testing>>Testing Your Database* section of the documentation. 35 | 36 | If you want to run the tests without using live database you can 37 | exclude @DatabaseLive group. Or make a copy of **phpunit.dist.xml** - 38 | call it **phpunit.xml** - and comment out the named "database". This will make 39 | the tests run quite a bit faster. 40 | 41 | ## Running the tests 42 | 43 | The entire test suite can be run by simply typing one command-line command from the main directory. 44 | 45 | > ./phpunit 46 | 47 | You can limit tests to those within a single test directory by specifying the 48 | directory name after phpunit. 49 | 50 | > ./phpunit app/Models 51 | 52 | ## Generating Code Coverage 53 | 54 | To generate coverage information, including HTML reports you can view in your browser, 55 | you can use the following command: 56 | 57 | > ./phpunit --colors --coverage-text=tests/coverage.txt --coverage-html=tests/coverage/ -d memory_limit=1024m 58 | 59 | This runs all of the tests again collecting information about how many lines, 60 | functions, and files are tested. It also reports the percentage of the code that is covered by tests. 61 | It is collected in two formats: a simple text file that provides an overview as well 62 | as a comprehensive collection of HTML files that show the status of every line of code in the project. 63 | 64 | The text file can be found at **tests/coverage.txt**. 65 | The HTML files can be viewed by opening **tests/coverage/index.html** in your favorite browser. 66 | 67 | ## PHPUnit XML Configuration 68 | 69 | The repository has a ``phpunit.xml.dist`` file in the project root that's used for 70 | PHPUnit configuration. This is used to provide a default configuration if you 71 | do not have your own configuration file in the project root. 72 | 73 | The normal practice would be to copy ``phpunit.xml.dist`` to ``phpunit.xml`` 74 | (which is git ignored), and to tailor it as you see fit. 75 | For instance, you might wish to exclude database tests, or automatically generate 76 | HTML code coverage reports. 77 | 78 | ## Test Cases 79 | 80 | Every test needs a *test case*, or class that your tests extend. CodeIgniter 4 81 | provides a few that you may use directly: 82 | * `CodeIgniter\Test\CIUnitTestCase` - for basic tests with no other service needs 83 | * `CodeIgniter\Test\CIDatabaseTestCase` - for tests that need database access 84 | 85 | Most of the time you will want to write your own test cases to hold functions and services 86 | common to your test suites. 87 | 88 | ## Creating Tests 89 | 90 | All tests go in the **tests/** directory. Each test file is a class that extends a 91 | **Test Case** (see above) and contains methods for the individual tests. These method 92 | names must start with the word "test" and should have descriptive names for precisely what 93 | they are testing: 94 | `testUserCanModifyFile()` `testOutputColorMatchesInput()` `testIsLoggedInFailsWithInvalidUser()` 95 | 96 | Writing tests is an art, and there are many resources available to help learn how. 97 | Review the links above and always pay attention to your code coverage. 98 | 99 | ### Database Tests 100 | 101 | Tests can include migrating, seeding, and testing against a mock or live1 database. 102 | Be sure to modify the test case (or create your own) to point to your seed and migrations 103 | and include any additional steps to be run before tests in the `setUp()` method. 104 | 105 | 1 Note: If you are using database tests that require a live database connection 106 | you will need to rename **phpunit.xml.dist** to **phpunit.xml**, uncomment the database 107 | configuration lines and add your connection details. Prevent **phpunit.xml** from being 108 | tracked in your repo by adding it to **.gitignore**. 109 | -------------------------------------------------------------------------------- /public/assets/img/error-404-monochrome.svg: -------------------------------------------------------------------------------- 1 | error-404-monochrome -------------------------------------------------------------------------------- /app/Views/objects/form_update.php: -------------------------------------------------------------------------------- 1 | extend('layout/templates'); ?> 2 | 3 | Section('content'); ?> 4 |
5 |
6 |
7 |

CRUD Object Model

8 | 13 |
14 |
15 | 16 | Form Update 17 |
18 |
19 | getFlashdata('failed'); 21 | if (!empty($errors)) : ?> 22 | 33 | 34 | 35 | 37 | 46 | 47 | 48 | 49 | 50 | 51 | 52 |
53 | 54 | 55 |
56 |
57 | 58 | 59 |
60 | > 61 | 64 |
65 |
66 | 67 | 70 |
71 | 72 |
73 | 74 | 77 |
78 |
79 | > 80 | 83 |
84 | 85 |
86 |
87 | 88 | 89 |
90 |
91 | 92 | 93 |
94 | Previous photo Previous photo 95 | 96 | Back 97 | 98 |
99 |
100 |
101 |
102 | endSection(); ?> -------------------------------------------------------------------------------- /app/Views/ajax/view_modal.php: -------------------------------------------------------------------------------- 1 | 2 | 62 | 63 | -------------------------------------------------------------------------------- /app/Views/objects/index.php: -------------------------------------------------------------------------------- 1 | extend('layout/templates'); ?> 2 | 3 | Section('content'); ?> 4 |
5 |
6 |
7 |

CRUD Object Model

8 | 12 |
13 |
14 | 15 | Students Table 16 |
17 |
18 | Add data 19 | 20 | getFlashdata('failed'); 22 | if (!empty($errors)) : ?> 23 | 34 | 35 | 36 | getFlashData('success')) : ?> 37 | 43 | 44 |
45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 59 | 60 | 61 | 62 | 63 | 64 | 67 | 75 | 76 | 77 | 78 |
NoNameGenderAddressPhotoOption
65 | photo 66 | 68 | 69 | Update 70 | 71 | 72 | Delete 73 | 74 |
79 |
80 |
81 |
82 |
83 |
84 | 85 | 86 | 87 | 110 | 111 | endSection(); ?> -------------------------------------------------------------------------------- /app/Views/ajax/view_modal_edit.php: -------------------------------------------------------------------------------- 1 | 2 | 63 | 64 | -------------------------------------------------------------------------------- /app/Views/layout/templates.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | <?= $title; ?> 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 45 | 46 | include('layout/sidebar'); ?> 47 | 48 | 49 | renderSection('content'); ?> 50 | 51 | 52 | 64 |
65 |
66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 95 | 96 | 97 | 116 | 117 | 118 | -------------------------------------------------------------------------------- /app/Config/UserAgents.php: -------------------------------------------------------------------------------- 1 | 'Windows 10', 18 | 'windows nt 6.3' => 'Windows 8.1', 19 | 'windows nt 6.2' => 'Windows 8', 20 | 'windows nt 6.1' => 'Windows 7', 21 | 'windows nt 6.0' => 'Windows Vista', 22 | 'windows nt 5.2' => 'Windows 2003', 23 | 'windows nt 5.1' => 'Windows XP', 24 | 'windows nt 5.0' => 'Windows 2000', 25 | 'windows nt 4.0' => 'Windows NT 4.0', 26 | 'winnt4.0' => 'Windows NT 4.0', 27 | 'winnt 4.0' => 'Windows NT', 28 | 'winnt' => 'Windows NT', 29 | 'windows 98' => 'Windows 98', 30 | 'win98' => 'Windows 98', 31 | 'windows 95' => 'Windows 95', 32 | 'win95' => 'Windows 95', 33 | 'windows phone' => 'Windows Phone', 34 | 'windows' => 'Unknown Windows OS', 35 | 'android' => 'Android', 36 | 'blackberry' => 'BlackBerry', 37 | 'iphone' => 'iOS', 38 | 'ipad' => 'iOS', 39 | 'ipod' => 'iOS', 40 | 'os x' => 'Mac OS X', 41 | 'ppc mac' => 'Power PC Mac', 42 | 'freebsd' => 'FreeBSD', 43 | 'ppc' => 'Macintosh', 44 | 'linux' => 'Linux', 45 | 'debian' => 'Debian', 46 | 'sunos' => 'Sun Solaris', 47 | 'beos' => 'BeOS', 48 | 'apachebench' => 'ApacheBench', 49 | 'aix' => 'AIX', 50 | 'irix' => 'Irix', 51 | 'osf' => 'DEC OSF', 52 | 'hp-ux' => 'HP-UX', 53 | 'netbsd' => 'NetBSD', 54 | 'bsdi' => 'BSDi', 55 | 'openbsd' => 'OpenBSD', 56 | 'gnu' => 'GNU/Linux', 57 | 'unix' => 'Unknown Unix OS', 58 | 'symbian' => 'Symbian OS', 59 | ]; 60 | 61 | // The order of this array should NOT be changed. Many browsers return 62 | // multiple browser types so we want to identify the sub-type first. 63 | public $browsers = [ 64 | 'OPR' => 'Opera', 65 | 'Flock' => 'Flock', 66 | 'Edge' => 'Spartan', 67 | 'Chrome' => 'Chrome', 68 | // Opera 10+ always reports Opera/9.80 and appends Version/ to the user agent string 69 | 'Opera.*?Version' => 'Opera', 70 | 'Opera' => 'Opera', 71 | 'MSIE' => 'Internet Explorer', 72 | 'Internet Explorer' => 'Internet Explorer', 73 | 'Trident.* rv' => 'Internet Explorer', 74 | 'Shiira' => 'Shiira', 75 | 'Firefox' => 'Firefox', 76 | 'Chimera' => 'Chimera', 77 | 'Phoenix' => 'Phoenix', 78 | 'Firebird' => 'Firebird', 79 | 'Camino' => 'Camino', 80 | 'Netscape' => 'Netscape', 81 | 'OmniWeb' => 'OmniWeb', 82 | 'Safari' => 'Safari', 83 | 'Mozilla' => 'Mozilla', 84 | 'Konqueror' => 'Konqueror', 85 | 'icab' => 'iCab', 86 | 'Lynx' => 'Lynx', 87 | 'Links' => 'Links', 88 | 'hotjava' => 'HotJava', 89 | 'amaya' => 'Amaya', 90 | 'IBrowse' => 'IBrowse', 91 | 'Maxthon' => 'Maxthon', 92 | 'Ubuntu' => 'Ubuntu Web Browser', 93 | 'Vivaldi' => 'Vivaldi', 94 | ]; 95 | 96 | public $mobiles = [ 97 | // legacy array, old values commented out 98 | 'mobileexplorer' => 'Mobile Explorer', 99 | // 'openwave' => 'Open Wave', 100 | // 'opera mini' => 'Opera Mini', 101 | // 'operamini' => 'Opera Mini', 102 | // 'elaine' => 'Palm', 103 | 'palmsource' => 'Palm', 104 | // 'digital paths' => 'Palm', 105 | // 'avantgo' => 'Avantgo', 106 | // 'xiino' => 'Xiino', 107 | 'palmscape' => 'Palmscape', 108 | // 'nokia' => 'Nokia', 109 | // 'ericsson' => 'Ericsson', 110 | // 'blackberry' => 'BlackBerry', 111 | // 'motorola' => 'Motorola' 112 | 113 | // Phones and Manufacturers 114 | 'motorola' => 'Motorola', 115 | 'nokia' => 'Nokia', 116 | 'palm' => 'Palm', 117 | 'iphone' => 'Apple iPhone', 118 | 'ipad' => 'iPad', 119 | 'ipod' => 'Apple iPod Touch', 120 | 'sony' => 'Sony Ericsson', 121 | 'ericsson' => 'Sony Ericsson', 122 | 'blackberry' => 'BlackBerry', 123 | 'cocoon' => 'O2 Cocoon', 124 | 'blazer' => 'Treo', 125 | 'lg' => 'LG', 126 | 'amoi' => 'Amoi', 127 | 'xda' => 'XDA', 128 | 'mda' => 'MDA', 129 | 'vario' => 'Vario', 130 | 'htc' => 'HTC', 131 | 'samsung' => 'Samsung', 132 | 'sharp' => 'Sharp', 133 | 'sie-' => 'Siemens', 134 | 'alcatel' => 'Alcatel', 135 | 'benq' => 'BenQ', 136 | 'ipaq' => 'HP iPaq', 137 | 'mot-' => 'Motorola', 138 | 'playstation portable' => 'PlayStation Portable', 139 | 'playstation 3' => 'PlayStation 3', 140 | 'playstation vita' => 'PlayStation Vita', 141 | 'hiptop' => 'Danger Hiptop', 142 | 'nec-' => 'NEC', 143 | 'panasonic' => 'Panasonic', 144 | 'philips' => 'Philips', 145 | 'sagem' => 'Sagem', 146 | 'sanyo' => 'Sanyo', 147 | 'spv' => 'SPV', 148 | 'zte' => 'ZTE', 149 | 'sendo' => 'Sendo', 150 | 'nintendo dsi' => 'Nintendo DSi', 151 | 'nintendo ds' => 'Nintendo DS', 152 | 'nintendo 3ds' => 'Nintendo 3DS', 153 | 'wii' => 'Nintendo Wii', 154 | 'open web' => 'Open Web', 155 | 'openweb' => 'OpenWeb', 156 | 157 | // Operating Systems 158 | 'android' => 'Android', 159 | 'symbian' => 'Symbian', 160 | 'SymbianOS' => 'SymbianOS', 161 | 'elaine' => 'Palm', 162 | 'series60' => 'Symbian S60', 163 | 'windows ce' => 'Windows CE', 164 | 165 | // Browsers 166 | 'obigo' => 'Obigo', 167 | 'netfront' => 'Netfront Browser', 168 | 'openwave' => 'Openwave Browser', 169 | 'mobilexplorer' => 'Mobile Explorer', 170 | 'operamini' => 'Opera Mini', 171 | 'opera mini' => 'Opera Mini', 172 | 'opera mobi' => 'Opera Mobile', 173 | 'fennec' => 'Firefox Mobile', 174 | 175 | // Other 176 | 'digital paths' => 'Digital Paths', 177 | 'avantgo' => 'AvantGo', 178 | 'xiino' => 'Xiino', 179 | 'novarra' => 'Novarra Transcoder', 180 | 'vodafone' => 'Vodafone', 181 | 'docomo' => 'NTT DoCoMo', 182 | 'o2' => 'O2', 183 | 184 | // Fallback 185 | 'mobile' => 'Generic Mobile', 186 | 'wireless' => 'Generic Mobile', 187 | 'j2me' => 'Generic Mobile', 188 | 'midp' => 'Generic Mobile', 189 | 'cldc' => 'Generic Mobile', 190 | 'up.link' => 'Generic Mobile', 191 | 'up.browser' => 'Generic Mobile', 192 | 'smartphone' => 'Generic Mobile', 193 | 'cellphone' => 'Generic Mobile', 194 | ]; 195 | 196 | // There are hundreds of bots but these are the most common. 197 | public $robots = [ 198 | 'googlebot' => 'Googlebot', 199 | 'msnbot' => 'MSNBot', 200 | 'baiduspider' => 'Baiduspider', 201 | 'bingbot' => 'Bing', 202 | 'slurp' => 'Inktomi Slurp', 203 | 'yahoo' => 'Yahoo', 204 | 'ask jeeves' => 'Ask Jeeves', 205 | 'fastcrawler' => 'FastCrawler', 206 | 'infoseek' => 'InfoSeek Robot 1.0', 207 | 'lycos' => 'Lycos', 208 | 'yandex' => 'YandexBot', 209 | 'mediapartners-google' => 'MediaPartners Google', 210 | 'CRAZYWEBCRAWLER' => 'Crazy Webcrawler', 211 | 'adsbot-google' => 'AdsBot Google', 212 | 'feedfetcher-google' => 'Feedfetcher Google', 213 | 'curious george' => 'Curious George', 214 | 'ia_archiver' => 'Alexa Crawler', 215 | 'MJ12bot' => 'Majestic-12', 216 | 'Uptimebot' => 'Uptimebot', 217 | ]; 218 | } 219 | -------------------------------------------------------------------------------- /app/Config/App.php: -------------------------------------------------------------------------------- 1 |