Version 1.3.5
WebGuard – Advanced PHP Login and User Management is a PHP application, written on CodeIgniter 4 framework with mysql database, which allows you to better manage your users and permissions. The application is also a useful script for developers who want to save many development hours when creating a framework from scratch.
It was designed to meet all user management needs, control permissions and following good development practices and data security, using framework and components always updated in the latest version.
You will need the following sofwares to customize this system.
Be careful when editing or customizing source code. If not edited correctly, the source code can break completely and introduce errors.
No support is provided for faulty customization.
The installation of the system using the installer is very simple, just follow the step by step below:
1- Validation - Check that all statuses are checked, if not, you need to correct to proceed with the installation. Click next to continue installation.
2 - Configuration - In this session, define the database, user and purchase code information in order to proceed.
3 - Finished - Your system has been successfully installed, don't forget to delete the installer folder in "public/install".
For installation on the local machine we recommend using WampServer or Xampp.
1 - Download WampServer
https://www.wampserver.com/en/
2 - Install and Configure WampServer
3 - Create VirtualHost in WampServer
4 - System Installer
Click here to see the step by step
1 - Download Xampp
https://www.apachefriends.org/
2 - Install and Configure Xampp
3 - Create VirtualHost in Xampp
4 - System Installer
Click here to see the step by step
Installing the system within a folder is very simple to configure, just follow the steps below:
This is the native version of the project with the standard Codeigniter 4 framework. We recommend using this version only for experienced developers as it does not have an installer like the other version.
Running the local project is very simple, just follow the step by step below:
We recommend using Postman to test the rest api.
1 - Download Postman
https://www.postman.com/downloads/
2 - Install and Configure Postman
3 - Download API Collection
Open the system and go to the tabs:
General > Api > Download Postman Collection
Below you can configure API usage settings, assign permissions and download the endpoint collection to PostMan.
Below shows how to assign API permissions to user groups.
To check the status of the service, just consult the example below:
How to get JWT token to access blocked methods, just follow the example below:
To authenticate a blocked method, you need to get the JWT token and set it as shown in the example below:
Open your ".env" file inside "/system/.env" and change the variable api.return = 'json' to api.return = 'xml'.
To enable the controllers to be public and not pass the login security validation, just follow the steps below:
The example below demonstrates adding a new controller named "Welcome".
Whenever you want to add a controller that will be public, just include it in the list below:
1 2 3 4 5 6 7 8 | <? php public function ignoreListController(){ return [ '', 'Welcome' ]; } ?> |
If you want to make this new controller the main controller when starting the system, just open the Routes.php file at:
/system/app/config/Routes.php
In the example below, the references to the "Home" controller were commented and the "Welcome" controller was defined as the main controller.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 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 62 63 64 65 66 67 68 69 70 71 72 | <? php namespace Config; // Create a new instance of our RouteCollection class. $ routes = Services ::routes(); // Load the system's routing file first, so that the app and ENVIRONMENT // can override as needed. if (file_exists(SYSTEMPATH . 'Config/Routes.php')) { require SYSTEMPATH . 'Config/Routes.php'; } /* * -------------------------------------------------------------------- * Router Setup * -------------------------------------------------------------------- */ $routes->setDefaultNamespace('App\Controllers'); //$routes->setDefaultController('Home'); $routes->setDefaultController('Welcome'); $routes->setDefaultMethod('index'); $routes->setTranslateURIDashes(false); $routes->set404Override(); $routes->setAutoRoute(true); /* * -------------------------------------------------------------------- * Route Definitions * -------------------------------------------------------------------- */ // We get a performance increase by specifying the default // route since we don't have to scan directories. //WEB ROUTER ------------------------------------------------------ //------------------------------------------------------------------ //$routes->get('/', 'Home::index'); $routes->get('/', 'Welcome::index'); $routes->get('lang/{locale}', 'Language::index'); //API ROUTER ------------------------------------------------------ //------------------------------------------------------------------ $routes->get('api/','Api::index'); $routes->get('api/status','Api::status'); $routes->post('api/signIn','Api::signIn'); //API ROUTER USER ------------------------------------------------------ //------------------------------------------------------------------ $routes->get('api/user/','Api::user/all'); $routes->get('api/user/(:segment)','Api::user/id/$1'); $routes->post('api/user/','Api::user/add'); $routes->put('api/user/(:segment)','Api::user/edit/$1'); $routes->delete('api/user/(:segment)','Api::user/delete/$1'); /* * -------------------------------------------------------------------- * Additional Routing * -------------------------------------------------------------------- * * There will often be times that you need additional routing and you * need it to be able to override any defaults in this file. Environment * based routes is one such time. require() additional route files here * to make that happen. * * You will have access to the $routes object within that file without * needing to reload it. */ if (file_exists(APPPATH . 'Config/' . ENVIRONMENT . '/Routes.php')) { require APPPATH . 'Config/' . ENVIRONMENT . '/Routes.php'; } ?> |
To enable the controllers to be public and not pass the login security validation, just follow the steps below:
Let's create a general to-do list example.
Inside the mysql database add your table eg:
1 2 3 4 5 6 7 8 | CREATE TABLE `task` ( `id_task` int(11) NOT NULL AUTO_INCREMENT, `title` varchar(150) COLLATE latin1_general_ci NOT NULL, `description` text COLLATE latin1_general_ci NOT NULL, `created_at` timestamp NOT NULL, `updated_at` timestamp NOT NULL ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id_task`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_general_ci; |
Inside the Models folder create the TaskModel.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | <? php namespace App\Models; class TaskModel extends BaseModel { protected $ table = 'task' ; protected $ primaryKey = 'id_task' ; protected $allowedFields = [ 'title', 'description' ]; protected $ useTimestamps = true ; protected $ createdField = 'created_at' ; protected $ updatedField = 'updated_at' ; } ?> |
Inside the Controllers folder create the Task.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 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 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 | <? php namespace App\Controllers; use App\Models\TaskModel; class Task extends BaseController { private $task_model; function __construct() { $this->task_model = new TaskModel(); } public function index() { $data['title'] = [ 'module' => 'Task List', 'page' => 'All Tasks', 'icon' => 'fas fa-tasks' ]; $data['breadcrumb'] = [ ['title' => 'Dashboard', 'route' => "/home", 'active' => false], ['title' => 'Task List', 'route' => "", 'active' => true] ]; $data['btn_add'] = [ 'title' => 'Add New Task', 'route' => '/task/add', 'class' => 'btn btn-lg btn-primary float-md-right', 'icon' => 'fas fa-plus' ]; $table = new \CodeIgniter\View\Table([ 'table_open' => '< table id = "table-grid" class = "table" >' ]); $table->setHeading('Title','Created at'); $data['grid'] = $this->task_model->select('title, created_at, id_task AS options') ->orderBy('created_at','DESC') ->findAll(); foreach ($data['grid'] as $key => $value){ $id = $data['grid'][$key]['options']; $data['grid'][$key]['options'] = ''. < button type = "button" class = "btn btn-primary btn-block dropdown-toggle" data-toggle = "dropdown" aria-haspopup = "true" aria-expanded = "false" > Options </ button > < div class = "dropdown-menu" > < a class = "dropdown-item" href = "/task/edit/'.$id.'" >< i class = "fas fa-edit" ></ i > Edit</ a > < button class = "dropdown-item" onclick = "delete_task(\''.$id.'\');" >< i class = "fas fa-trash" ></ i > Delete</ button > </ div > </ div > '; } $data['table'] = $table; echo view(getenv('theme.path').'main/header'); echo view(getenv('theme.path').'form/task/index',$data); echo view(getenv('theme.path').'main/footer'); } public function add() { helper('form'); $data['title'] = [ 'module' => 'Add Task', 'page' => 'Add Task', 'icon' => 'far fa-plus-square' ]; $data['breadcrumb'] = [ ['title' => 'Dashboard', 'route' => "/home", 'active' => false], ['title' => 'List Task', 'route' => "/task", 'active' => false], ['title' => 'Add Task', 'route' => "", 'active' => true] ]; $data['btn_return'] = [ 'title' => 'Come Back', 'route' => '/task', 'class' => 'btn btn-dark mr-1', 'icon' => 'fas fa-angle-left' ]; $data['btn_submit'] = [ 'title' => 'Save', 'route' => '', 'class' => 'btn btn-primary mr-1', 'icon' => 'fas fa-save' ]; echo view(getenv('theme.path').'main/header'); echo view(getenv('theme.path').'form/task/form',$data); echo view(getenv('theme.path').'main/footer'); } public function edit($token=null) { if(empty($token)){ return redirect()->to('/task'); } helper('form'); $data['title'] = [ 'module' => 'Edit Task', 'page' => 'Edit Task', 'icon' => 'fas fa-edit' ]; $data['breadcrumb'] = [ ['title' => 'Dashboard', 'route' => "/home", 'active' => false], ['title' => 'List Task', 'route' => "/task", 'active' => false], ['title' => 'Edit Task', 'route' => "", 'active' => true] ]; $data['btn_return'] = [ 'title' => 'Come Back', 'route' => '/task', 'class' => 'btn btn-dark mr-1', 'icon' => 'fas fa-angle-left' ]; $data['btn_submit'] = [ 'title' => 'Save', 'route' => '', 'class' => 'btn btn-primary mr-1', 'icon' => 'fas fa-save' ]; $data['obj'] = $this->task_model->where('id_task', $token)->first(); if($data['obj']==null){ return redirect()->to('/task'); } echo view(getenv('theme.path').'main/header'); echo view(getenv('theme.path').'form/task/form',$data); echo view(getenv('theme.path').'main/footer'); } public function store() { $session = session(); helper('form'); $rules = [ 'title' => 'required', 'description' => 'required' ]; $rules_error = [ 'title' => [ 'required' => 'Title field is required.', ], 'description' => [ 'required' => 'Description field is required', ] ]; if ($this->validate($rules,$rules_error)){ if(empty($this->request->getPost('id_task'))){ $this->task_model->save([ 'id_task' => null, 'title' => $this->request->getPost('title'), 'description' => $this->request->getPost('description') ]); $session->setFlashdata('sweet', ['success','Added successfully!']); return redirect()->to('/task'); }else{ $this->task_model->save([ 'id_task' => $this->request->getPost('id_task'), 'title' => $this->request->getPost('title'), 'description' => $this->request->getPost('description') ]); $session->setFlashdata('sweet', ['success','Successfully edited!']); return redirect()->to('/task'); } }else{ $session->setFlashdata('error','error'); $this->add(); } } public function delete($id) { $session = session(); if(!empty($session->get('token'))){ $this->task_model->where('id_task', $id)->delete(); $session->setFlashdata('sweet', ['success','Successfully deleted!']); return redirect()->to('/task'); }else{ return redirect()->to('/login'); } } } ?> |
Inside the Views/themes/focus2/form/ folder create the "task" folder and inside it create two index.php and form.php files
index.php1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 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 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 | <!--Content Body--> < div class = "content-body" > < div class = "container-fluid" > < div class = "row page-titles mx-0" > < div class = "col-sm-6 p-md-0" > < div class = "welcome-text" > < h4 >< i class="<?= $title['icon']??'' ?>"></ i > <?= $title['module']??'' ?></ h4 > < span class = "ml-1" ><?= $title['page']??'' ?></ span > </ div > </ div > < div class = "col-sm-6 p-md-0 justify-content-sm-end mt-2 mt-sm-0 d-flex" > < ol class = "breadcrumb" > <? php foreach ($breadcrumb??[] as $item) : ?> <? php if (!$item['active']) : ?> < li class = "breadcrumb-item" >< a href="<?= $item['route'] ?>"><?= $item['title'] ?></ a ></ li > <? php else : ?> < li class = "breadcrumb-item active" ><?= $item['title'] ?></ li > <? php endif; ?> <? php endforeach; ?> </ ol > </ div > </ div > < div class = "row" > < div class = "col-12" > < div class = "card" > < div class = "card-header row" > < div class = "col-sm-6" > < h4 class = "card-title" ><?= $title['page']??'' ?></ h4 > </ div > < div class = "col-sm-6 justify-content-sm-end mt-2 mt-sm-0 d-flex" > < a href="<?= $btn_add['route']??'#'?>" class="<?= $btn_add['class']??''?>"> < i class="<?= $btn_add['icon']??'' ?>"></ i > <?= $btn_add['title']??'' ?> </ a > </ div > </ div > < div class = "card-body" > < div class = "table-responsive" > <? php use CodeIgniter\View\Table; $table = $table??new Table(); echo $table->generate($grid??[]); ?> </ div > </ div > </ div > </ div > </ div > </ div > </ div > <!-- Required vendors --> < script src = "/themes/focus2/vendor/global/global.min.js" ></ script > < script src = "/themes/focus2/js/quixnav-init.js" ></ script > < script src = "/themes/focus2/js/custom.min.js" ></ script > <!-- Datatable --> < script src = "/themes/focus2/vendor/datatables/js/jquery.dataTables.min.js" ></ script > < script src = "/themes/focus2/vendor/pickers/daterange/moment.min.js" ></ script > < script src = "/themes/focus2/vendor/datatables/js/dataTables.datetime.js" ></ script > < script src = "/themes/focus2/vendor/datatables/js/dataTables.buttons.min.js" ></ script > < script src = "/themes/focus2/vendor/datatables/js/buttons.bootstrap4.min.js" ></ script > < script src = "/themes/focus2/vendor/datatables/js/jszip.min.js" ></ script > < script src = "/themes/focus2/vendor/datatables/js/pdfmake.min.js" ></ script > < script src = "/themes/focus2/vendor/datatables/js/vfs_fonts.js" ></ script > < script src = "/themes/focus2/vendor/datatables/js/buttons.html5.min.js" ></ script > < script src = "/themes/focus2/vendor/datatables/js/buttons.print.min.js" ></ script > < script src = "/themes/focus2/vendor/datatables/js/buttons.colVis.min.js" ></ script > <!-- Alert --> < script src = "/themes/focus2/vendor/sweetalert2/dist/sweetalert2.min.js" ></ script > < script src = "/themes/focus2/vendor/toastr/js/toastr.min.js" ></ script > <!-- Custom --> < script src = "/assets/js/main.js" ></ script > < script > $(document).ready(function () { "use strict"; let data = [ { targets: 1, render: $.fn.dataTable.render.moment('YYYY-MM-DD HH:mm:ss','<?=momentDateTimeJS()?>') } ]; let order = [[0, "asc"]]; let translate = '/themes/focus2/vendor/datatables/locales/<?=langJS()?>.json'; let button = ["<?=lang("App.global_copy")?>","<?=lang("App.global_print")?>","<?=lang("App.global_excel")?>","<?=lang("App.global_pdf")?>"]; loadDataTable('table-grid', '', translate, true, true, order, data, button); }); function delete_task(id){ "use strict"; swal({ title: "Are you sure you want to delete?", text: "You will not be able to retrieve this information!", type: "warning", showCancelButton: !0, confirmButtonColor: "#f34141", confirmButtonText: "Yes, Delete!", cancelButtonText: "Cancel", closeOnConfirm: !1 }).then(function(isConfirm) { console.log(isConfirm); if (isConfirm.value) { window.location.href = '/task/delete/'+id; } }) } </ script > <?= sweetAlert() ?> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 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 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 | <!--Content Body--> < div class = "content-body" > < div class = "container-fluid" > < div class = "row page-titles mx-0" > < div class = "col-sm-6 p-md-0" > < div class = "welcome-text" > < h4 >< i class="<?= $title['icon']??'' ?>"></ i > <?= $title['module']??'' ?></ h4 > < span class = "ml-1" ><?= $title['page']??'' ?></ span > </ div > </ div > < div class = "col-sm-6 p-md-0 justify-content-sm-end mt-2 mt-sm-0 d-flex" > < ol class = "breadcrumb" > <? php foreach ($breadcrumb??[] as $item) : ?> <? php if (!$item['active']) : ?> < li class = "breadcrumb-item" >< a href="<?= $item['route'] ?>"><?= $item['title'] ?></ a ></ li > <? php else : ?> < li class = "breadcrumb-item active" ><?= $item['title'] ?></ li > <? php endif; ?> <? php endforeach; ?> </ ol > </ div > </ div > < div class = "row" > < div class = "col-12" > < div class = "card" > < div class = "card-header" > < h4 class = "card-title" ><?= $title['page']??'' ?></ h4 > </ div > < div class = "card-body" > <?= formAlert() ?> < form class = "form" action = "/task/store" method = "post" > <?= csrf_field() ?> < input type = "hidden" name = "id_task" value="<?= (isset($obj)) ? $obj['id_task'] : set_value('id_task') ?>"> < div class = "form-body" > < div class = "row" > < div class = "col-md-12" > < div class = "form-group" > < label for = "title" class = "text-dark" >Title</ label > < input type = "text" name = "title" id = "title" class = "form-control" value="<?= (isset($obj)) ? $obj['title'] : set_value('title') ?>"> </ div > </ div > < div class = "col-md-12" > < div class = "form-group" > < label class = "text-dark" >Description</ label > < textarea class = "form-control" id = "description" name = "description" rows = "3" ><?= (isset($obj)) ? $obj['description'] : set_value('description') ?></ textarea > </ div > </ div > </ div > </ div > < div class = "form-actions mt-2" > < a href="<?= $btn_return['route']??'#' ?>" class="<?= $btn_return['class']??''?>"> < i class="<?= $btn_return['icon']??'' ?>"></ i > <?= $btn_return['title']??'' ?> </ a > < button type = "submit" class="<?= $btn_submit['class']??''?>"> < i class="<?= $btn_submit['icon']??'' ?>"></ i > <?= $btn_submit['title']??'' ?> </ button > </ div > </ form > </ div > </ div > </ div > </ div > </ div > </ div > <!-- Required vendors --> < script src = "/themes/focus2/vendor/global/global.min.js" ></ script > < script src = "/themes/focus2/js/quixnav-init.js" ></ script > < script src = "/themes/focus2/js/custom.min.js" ></ script > < script src = "/themes/focus2/vendor/select2/js/select2.full.min.js" ></ script > <!-- Form --> < script > $(document).ready(function () { "use strict"; $('#title').focus(); }); </ script > |
To enable the controllers to be public and not pass the login security validation, just follow the steps below:
1 2 3 4 | < a href="<?= site_url('lang/en'); ?>" class="dropdown-item"> < img src = "/assets/flags/us_32_circle.png" > < span class = "ml-2" ><?= lang("App.lang_en") ?></ span > </ a > |
Below you will learn how to create and install an add-on.
There is an add-on called "Tasks", it is a basis on how to create an add-on, you can use it as a basis to create yours.
Add-ons in Codeigniter 4 follow the same folder pattern as the main application.
Your add-on information is added to the app.json file.
Create table, insert, update or delete are added to SQL files.
Below shows how to configure app.json
Open the modules and upload the .zip file for the addon you want to use.
After loading, it will display a success message and list the new add-on.
To view the addon in the menu, you need to assign access permission to the addon, as shown below:
Assign the groups that can have access to this addon.
After assigning the permissions correctly, simply save and update the page that will display the addon menus in the system menu.
Access the settings and activate as shown in the image below:
After activating the settings, each user must access their profile and activate the 2FA function as shown in the image below:
After activating, the user must save the backup keys and the secret key.
Remembering that you must scan the QRCode with the Google Authenticator application or another similar application to be able to generate dynamic OTP codes.
Below shows how to assign information to each provider and the return url in the system.
WebGuard uses the "HybridAuth" library to assign several providers, if you want to add more providers or see the documentation, just click here.
CodeIgniter is a PHP application development framework.
Currently the system uses version 4.4.3
1 2 3 4 5 6 7 8 9 10 11 | ----------------------------------------------------------------------------------------- Version 1.4.0 ----------------------------------------------------------------------------------------- - CodeIgniter Update - CRUD Generator. - Page Generator - IP Manager. - Session Manager. - God mode. - Create dynamic dashboards. - Uploading new layouts to the backend. |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | ----------------------------------------------------------------------------------------- Version 1.3.5 - 12 November 2023 ----------------------------------------------------------------------------------------- - CodeIgniter v4.4.3. - Fixed Add-on Permissions. - Focus2 Dark Theme (Backend). - Adjustment in PT and ES Translation. - Adjustment in the user menu. - Adjustment in the frontend menu. - Added RTL Support (Backend) - Added Arabic language. - Adjusted when deleting account from profile. - Adjusted confirmation email tag. - Documentation has been improved and updated. |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | ----------------------------------------------------------------------------------------- Version 1.3.0 - 08 July 2023 ----------------------------------------------------------------------------------------- - CodeIgniter v4.3.6. - Created the Frontend page. - Enable and Disable Frontend. - Pusher notification. - Add rules in the API. - International Telephone Input. - GDPR Cookies, Data and Deletion. - Allow the API to select more than one permission group. - Creation of Modules. - Access permissions on modules. - Allow to disable the use of modules. - Using HMVC with WebGuard. - Other minor improvements. |
1 2 3 4 5 6 7 8 9 10 | ----------------------------------------------------------------------------------------- Version 1.2.1 - 28 June 2022 ----------------------------------------------------------------------------------------- - Fixed responsive in table grids. - Fixed Side-Menu and button transparency reported by Diego. - Fixed the "oAuth Authentication" screen the responsive part. - Updated CSS and JS from Focus2 template. - CodeIgniter v4.2.1 - Composer Updated all components. - Field Country and User Language and Edited Profile |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | ----------------------------------------------------------------------------------------- Version 1.2.0 - 29 April 2022 ----------------------------------------------------------------------------------------- - Added DataGrid pagination through the database in data blocks. - Added description of the permissions group in the users list. - Added email sending test in settings. - Fixes of "use strict" in DataGrid. - Fixes in notifications and alerts. - Fixes in sending e-mail. - Fixes in user profile update. - Fixes when deleting user allows to delete their activities together. - Fixes in recaptcha and hcaptcha. - Updated all composer components. - Framework update for CodeIgniter v4.1.9. - Among other Fixes. |
1 2 3 4 5 6 | ----------------------------------------------------------------------------------------- Version 1.1.0 - 04 February 2022 ----------------------------------------------------------------------------------------- - Documentation Update. - PHP 8.X.X support. - Framework Update for CodeIgniter v4.1.8. |
1 2 3 4 | ----------------------------------------------------------------------------------------- Version 1.0.0 - 16 January 2022 ----------------------------------------------------------------------------------------- - Initial Release. |