From 5beb6a41641b7aba6691ef305510cd0f9b4661c4 Mon Sep 17 00:00:00 2001 From: Eli Tokar <52536795+elitGithub@users.noreply.github.com> Date: Wed, 22 Jun 2022 23:49:12 +0300 Subject: [PATCH 001/100] fix not necessary double equal sign --- engine/Core.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/engine/Core.php b/engine/Core.php index c738b7de..c16c584f 100644 --- a/engine/Core.php +++ b/engine/Core.php @@ -22,11 +22,10 @@ private function serve_vendor_asset() { $vendor_file_path = explode('/vendor/', ASSUMED_URL)[1]; $vendor_file_path = '../vendor/'.$vendor_file_path; if (file_exists($vendor_file_path)) { - $content_type = mime_content_type($vendor_file_path); if (strpos($vendor_file_path, '.css')) { $content_type = 'text/css'; } else { - $content_type == 'text/plain'; + $content_type = 'text/plain'; } header('Content-type: '.$content_type); From a28c848c915fd3ed8daea5343bbb3ef7407f6eb5 Mon Sep 17 00:00:00 2001 From: David Connelly Date: Fri, 24 Jun 2022 11:34:59 +0100 Subject: [PATCH 002/100] A few tweaks and corrections to do with the form validation helper. No big deal. --- engine/Api.php | 6 ++++++ engine/Model.php | 2 +- engine/tg_helpers/validation_helper.php | 10 +++++----- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/engine/Api.php b/engine/Api.php index 4e5b37ef..f838bba5 100755 --- a/engine/Api.php +++ b/engine/Api.php @@ -603,6 +603,12 @@ function create() { $post = file_get_contents('php://input'); $decoded = json_decode($post, true); + if (gettype($decoded) !== 'array') { + http_response_code(400); + echo 'No posted values have been received!'; + die(); + } + if (count($decoded)>0) { $params = $this->_get_params_from_post($decoded); } else { diff --git a/engine/Model.php b/engine/Model.php index 64081734..7a82b1a7 100755 --- a/engine/Model.php +++ b/engine/Model.php @@ -399,7 +399,7 @@ public function insert($data, $target_tbl=NULL) { $target_tbl = $this->get_table_from_url(); } - $sql = 'INSERT INTO '.$target_tbl.' ('; + $sql = 'INSERT INTO `'.$target_tbl.'` ('; $sql.= '`'.implode("`, `", array_keys($data)).'`)'; $sql.= ' VALUES ('; diff --git a/engine/tg_helpers/validation_helper.php b/engine/tg_helpers/validation_helper.php index e5b4e366..2ed800c2 100755 --- a/engine/tg_helpers/validation_helper.php +++ b/engine/tg_helpers/validation_helper.php @@ -15,7 +15,7 @@ public function set_rules($key, $label, $rules) { $posted_value = $_FILES[$key]; $tests_to_run[] = 'validate_file'; } else { - $posted_value = $_POST[$key]; + $posted_value = isset($_POST[$key]) ? $_POST[$key] : ''; $tests_to_run = $this->get_tests_to_run($rules); } @@ -390,11 +390,11 @@ private function unique($key, $label, $posted_value, $inner_value=null) { $bits = explode(',', $inner_value); if (count($bits) == 2) { - $table_name = $bits[0]; - $allowed_id = $bits[1]; + $allowed_id = $bits[0]; + $table_name = $bits[1]; } else { - $table_name = SEGMENTS[1]; $allowed_id = $inner_value; + $table_name = SEGMENTS[1]; } settype($allowed_id, 'int'); @@ -409,7 +409,7 @@ private function unique($key, $label, $posted_value, $inner_value=null) { $row_id = $row->id; $row_target_value = $row->$key; if (($row->id !== $allowed_id) && ($row->$key == $posted_value)) { - $this->form_submission_errors[] = 'The ' . $label . ' field is already on our system.'; + $this->form_submission_errors[] = 'The ' . $label . ' that you submitted is already on our system.'; break; } } From 49985875d56572b41af92948ae65ae2e4512297a Mon Sep 17 00:00:00 2001 From: David Connelly Date: Fri, 24 Jun 2022 12:07:17 +0100 Subject: [PATCH 003/100] Previously, Trongate websites without a properly configured database were generating errors. Now, the model file does not run if the DATABASE value in config is empty. This means that it's now possible to have Trongate websites that do not have a database. --- engine/Model.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/engine/Model.php b/engine/Model.php index 7a82b1a7..367ed90c 100755 --- a/engine/Model.php +++ b/engine/Model.php @@ -16,6 +16,10 @@ class Model { public function __construct($current_module = NULL) { + if (DATABASE == '') { + return; + } + $this->port = (defined('PORT') ? PORT : '3306'); $this->current_module = $current_module; From 12006b1a2c18a9e6ee844acbd9ff5c0cab86b8d9 Mon Sep 17 00:00:00 2001 From: David Connelly Date: Tue, 28 Jun 2022 17:01:05 +0100 Subject: [PATCH 004/100] Correction to APPPATH and updated readme file. --- README.md | 13 ++++++++++++- config/config.php | 21 ++++++++++----------- 2 files changed, 22 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 74e93204..918fcbe5 100755 --- a/README.md +++ b/README.md @@ -1 +1,12 @@ -Every time somebody gives the Trongate framework a star on Github, middle managers and self appointed PHP aficionados start crying like babies. Do something amazing today. Give Trongate a star on GitHub and together we SHALL make PHP great again! https://github.com/davidjconnelly/trongate-framework \ No newline at end of file +# *** PLEASE GIVE TRONGATE A STAR ON GITHUB *** + +### GitHub stars are the metric by which the success of frameworks gets measured. We need 1,200 GitHub stars to make Trongate a top ten PHP framework. If Trongate becomes a top ten PHP framework it will be one of the most electrifying events in the history of PHP! + +Help us to achieve our goal and together we SHALL make PHP great again! + +The GitHub URL for Trongate is: +## [https://github.com/davidjconnelly/trongate-framework](https://github.com/davidjconnelly/trongate-framework) + +Thank you and may the code be with you! + +-David Connelly (founder) \ No newline at end of file diff --git a/config/config.php b/config/config.php index 3b685476..55891e9b 100755 --- a/config/config.php +++ b/config/config.php @@ -1,19 +1,18 @@ Date: Wed, 29 Jun 2022 10:28:57 +0100 Subject: [PATCH 005/100] Simplified config.php by moving APPPATH and REQUEST_TYPE definitions to the bootstrap.php file. Since they never get changed, there's no point in having them inside config.php! --- config/config.php | 2 -- engine/bootstrap.php | 2 ++ 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/config/config.php b/config/config.php index 55891e9b..be3e075f 100755 --- a/config/config.php +++ b/config/config.php @@ -21,6 +21,4 @@ define('DEFAULT_MODULE', 'welcome'); define('DEFAULT_CONTROLLER', 'Welcome'); define('DEFAULT_METHOD', 'index'); -define('APPPATH', str_replace("\\", "/", dirname(dirname(__FILE__)).'/')); -define('REQUEST_TYPE', $_SERVER['REQUEST_METHOD']); define('MODULE_ASSETS_TRIGGER', '_module'); \ No newline at end of file diff --git a/engine/bootstrap.php b/engine/bootstrap.php index e04d211d..155c6abb 100755 --- a/engine/bootstrap.php +++ b/engine/bootstrap.php @@ -40,5 +40,7 @@ function load($template_file, $data=NULL) { } } +define('APPPATH', str_replace("\\", "/", dirname(dirname(__FILE__)).'/')); +define('REQUEST_TYPE', $_SERVER['REQUEST_METHOD']); $tg_helpers = ['form_helper', 'flashdata_helper', 'url', 'validation_helper']; define('TRONGATE_HELPERS', $tg_helpers); \ No newline at end of file From 03979439a9fa444e45f9a6fb8e23f0daf53fe0a2 Mon Sep 17 00:00:00 2001 From: TATIYA Date: Sat, 9 Jul 2022 21:51:07 +0100 Subject: [PATCH 006/100] update readme file New readme page --- README.md | 212 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 205 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 918fcbe5..3ce37e70 100755 --- a/README.md +++ b/README.md @@ -1,12 +1,210 @@ -# *** PLEASE GIVE TRONGATE A STAR ON GITHUB *** +
+

Trongate PHP Framework

+ logo +

Break the rules use Trongate

+ +

+ GitHub stars are the metric by which the success of frameworks gets measured. We need 1,200 GitHub stars to make Trongate a top ten PHP framework. If Trongate becomes a top ten PHP framework it will be one of the most electrifying events in the history of PHP! +

-### GitHub stars are the metric by which the success of frameworks gets measured. We need 1,200 GitHub stars to make Trongate a top ten PHP framework. If Trongate becomes a top ten PHP framework it will be one of the most electrifying events in the history of PHP! + + +

+ + contributors + + + last update + + + forks + + + stars + + + open issues + +

+ +

+ Website + · + Documentation + · + Report Bug + · + Request Feature +

+
-Help us to achieve our goal and together we SHALL make PHP great again! +
-The GitHub URL for Trongate is: -## [https://github.com/davidjconnelly/trongate-framework](https://github.com/davidjconnelly/trongate-framework) + +# :notebook_with_decorative_cover: Table of Contents -Thank you and may the code be with you! +- [About the Project](#star2-about-the-project) + * [Features](#dart-features) +- [Getting Started](#toolbox-getting-started) + * [Prerequisites](#bangbang-prerequisites) + * [Installation](#gear-installation) +- [Usage](#eyes-usage) +- [Contributing](#wave-contributing) +- [License](#warning-license) +- [Contact](#handshake-contact) + --David Connelly (founder) \ No newline at end of file + +## :star2: About the Project + +
+

Trongate was built with a love of pure PHP and a belief that PHP is best when it's easy, stable and fast. Trongate uses PHP the way that PHP was originally intended to be used, i.e., as a templating engine. Trongate is the only PHP framework that actively rejects; PSR-4 auto-loading, Composer, Packagist, rewrite culture, certification and all forms of bureaucracy.

+

+Trongate is also the first PHP framework that has been built with a determination to remain v1 forever. Finally, PHP developers have framework that has been built with stability as a priority.

+ +

The Trongate framework will never contain features that developers don't need. Trongate will never offer certification - paid or unpaid. Instead, Trongate will continue to boldly tackle real problems that face real developers every day.

+ +

The Trongate framework is free. The Trongate desktop app is also free. These things will always be free. That's a promise!

+

Is this fast enough for you?

+

Trongate favours pure PHP over things like PSR-4 autoloading. The result? A brave new PHP framework that demolishes the competition based on every metric that counts. Pure PHP is back!

+ +
+ + +### :dart: Features + +
+ Automatic Installer +

+ No command line. No Git. No 'Composer dot phar'. No Yaml. No Packagist. Trongate installs itself. Simply download the free Trongate desktop app and set up entire database driven apps in seconds! +

+
+
+ API Manager +

+ Trongate's best in class API Manager sets a new standard for rapid API development. Mobile developers and JavaScript developers - you're in for a treat! +

+
+
+ Advanced File Uploaders +

+ Trongate sets a new standard for PHP by letting developers build highly performant features in record time. This includes some of the fastest file uploaders in the industry. +

+
+
+ Graphical Query Builder +

+ Trongate is the only framework that comes with a free graphical SQL query builder. So, now you can build complex table joins easily and liberate yourself from costly db mangement software. +

+
+
+ Updates Itself +

+ Whenever new features come out you get an alert, asking if you want it. If you want it, you click 'yes'. Then it self-updates. Another industry first! By the way, Trongate is also the first PHP framework that aims to be v1 forever! +

+
+
+ Automatic Code Generator +

+ With Trongate, you get a revolutionary code generator to help take care of boring boilerplate code. Your productivity levels are about to go through the roof! +

+
+
+ JavaScript Components +

+ With Trongate you can build beautiful pop up calendars, time pickers, date-range pickers and even drag and drop file uploaders in seconds! +

+
+
+ Custom Themes +

+ Unlike some frameworks that tether users to a particular design, Trongate lets you choose from a potentially unlimited number of design themes for your admin panels. +

+
+
+ Custom CSS Library +

+ Frameworks should NOT depend upon third party libraries. That's why Trongate has its own CSS library. Yet another industry first! +

+
+
+ Modular HAVC +

+ Trongate uses Hierarchical Asset View Controller architecture. It's like HMVC on steroids! Finally, the PHP community have a "truely modular" PHP framework. +

+
+
+ Module Import Wizard +

+ Easily import entire modules, including SQL data. Drag 'n' drop web development, at last. +

+
+
+ An App Store! +

+ Trongate's Module Market is the PHP community's first legitimate challenge to the Composer / Packagist combo. Goodbye henhouse. Hello sunshine! +

+
+ + +## :toolbox: Getting Started + + +### :bangbang: Prerequisites +
    +
  • PHP
  • +
  • MariaDB or MySQL
  • +
+ + +### :gear: Installation + +The Trongate PHP framework is available from GitHub at the following URL: + +https://github.com/davidjconnelly/trongate-framework. + +However, the fastest and easiest way to get the Trongate PHP framework is by using the Trongate Desktop App. The Trongate Desktop App is 100% free of charge (it always will be!) and is available for Mac, Windows and Linux. The Trongate Desktop App can be downloaded from: + +https://trongate.io/download.​ + +There are a wide variety of benefits from using the Trongate Desktop App. These include: +
    +
  • Automatic framework downloads and setups
  • +
  • Automatic updates whenever new features are added to Trongate
  • +
  • Automatic code generation
  • +
  • Automatic database management
  • +
  • A graphical query builder
  • +
+ +

and much more... !

+ + + + + +## :eyes: Usage + + + Watch the video + + + +## :wave: Contributing + + + + + + +Contributions are always welcome! + + +## :warning: License + +See LICENSE.txt for more information. + + + +## :handshake: Contact + +David Connelly - [@davidjconnelly](https://twitter.com/davidjconnelly) - [Get In Touch](https://trongate.io/your_messages/compose) From ac10eba8ef36a5e7806e0c6da0de4fa954df2f60 Mon Sep 17 00:00:00 2001 From: Simon Field Date: Fri, 22 Jul 2022 00:44:50 +1000 Subject: [PATCH 007/100] Close model with Escape key Adds closing of Modal with an escape key press - in both admin.js and app.js --- public/js/admin.js | 15 ++++++++++++++- public/js/app.js | 14 +++++++++++++- 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/public/js/admin.js b/public/js/admin.js index 52f6b004..8419d8d9 100755 --- a/public/js/admin.js +++ b/public/js/admin.js @@ -74,6 +74,17 @@ function closeModal() { overlay.remove(); } +function attemptEscCloseModal () { + document.onkeydown = function (e) { + + var modalContainer = document.getElementById('modal-container'); + + if ((e.key == "Escape") && (modalContainer)) { + closeModal(); + } + }; +} + function fetchAssociatedRecords(relationName, updateId) { var params = { @@ -434,4 +445,6 @@ body.addEventListener('click', (ev) => { closeSlideNav(); } } -}); \ No newline at end of file +}); + +attemptEscCloseModal (); \ No newline at end of file diff --git a/public/js/app.js b/public/js/app.js index 61f029f7..d0063f92 100755 --- a/public/js/app.js +++ b/public/js/app.js @@ -84,6 +84,17 @@ function closeModal() { overlay.remove(); } +function attemptEscCloseModal () { + document.onkeydown = function (e) { + + var modalContainer = document.getElementById('modal-container'); + + if ((e.key == "Escape") && (!modalContainer)) { + closeModal(); + } + }; +} + var slideNavLinks = document.querySelector("#slide-nav ul"); if (slideNavLinks !== null) { @@ -102,5 +113,6 @@ if (slideNavLinks !== null) { } } }); +} -} \ No newline at end of file +attemptEscCloseModal (); \ No newline at end of file From 18900af6051e3c08b92522f2c607ac2629312594 Mon Sep 17 00:00:00 2001 From: Simon Field Date: Fri, 22 Jul 2022 01:03:33 +1000 Subject: [PATCH 008/100] Minor tweak to shorten code Shorten code on document.getElementById('modal-container') to _('modal-container') --- public/js/admin.js | 2 +- public/js/app.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/public/js/admin.js b/public/js/admin.js index 8419d8d9..7b1d9094 100755 --- a/public/js/admin.js +++ b/public/js/admin.js @@ -77,7 +77,7 @@ function closeModal() { function attemptEscCloseModal () { document.onkeydown = function (e) { - var modalContainer = document.getElementById('modal-container'); + var modalContainer = _("modal-container"); if ((e.key == "Escape") && (modalContainer)) { closeModal(); diff --git a/public/js/app.js b/public/js/app.js index d0063f92..94cb0b7f 100755 --- a/public/js/app.js +++ b/public/js/app.js @@ -87,7 +87,7 @@ function closeModal() { function attemptEscCloseModal () { document.onkeydown = function (e) { - var modalContainer = document.getElementById('modal-container'); + var modalContainer = _("modal-container"); if ((e.key == "Escape") && (!modalContainer)) { closeModal(); From ad0e081667801d345e1ca6a1dd50e2f431aa7934 Mon Sep 17 00:00:00 2001 From: Giulio Prina Ricotti Date: Sun, 7 Aug 2022 19:11:26 +0200 Subject: [PATCH 009/100] README.md: fix download link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3ce37e70..a635c5d1 100755 --- a/README.md +++ b/README.md @@ -165,7 +165,7 @@ https://github.com/davidjconnelly/trongate-framework. However, the fastest and easiest way to get the Trongate PHP framework is by using the Trongate Desktop App. The Trongate Desktop App is 100% free of charge (it always will be!) and is available for Mac, Windows and Linux. The Trongate Desktop App can be downloaded from: -https://trongate.io/download.​ +https://trongate.io/download There are a wide variety of benefits from using the Trongate Desktop App. These include:
    From 7fb7ad84deefc1de571f1810215769fe6f67c4ed Mon Sep 17 00:00:00 2001 From: ReDeLe-design <72892858+ReDeLe-design@users.noreply.github.com> Date: Sun, 14 Aug 2022 23:38:22 +0200 Subject: [PATCH 010/100] Update trongate-datetime.js use the new functions parseDate and parseDateTime to parse the input into a Date Object, using the format of the spezified locale in the global variable "localeString". Also use the locale to output the Date or DateTime as aspected. --- public/js/trongate-datetime.js | 154 ++++++++++++++++++++++++--------- 1 file changed, 115 insertions(+), 39 deletions(-) diff --git a/public/js/trongate-datetime.js b/public/js/trongate-datetime.js index b3cfd7ba..9c19e099 100755 --- a/public/js/trongate-datetime.js +++ b/public/js/trongate-datetime.js @@ -1,6 +1,9 @@ // DATE/TIME PICKER SETTINGS START : PLEASE FEEL FREE TO CHANGE THE SETTINGS BELOW THIS LINE: var weekStartsOn = 1; // 0 = Sunday, 1 = Monday + +// the localeString spezifies the locale used to parse the date and to output the date var localeString = 'en-GB'; //details: https://www.w3schools.com/jsref/jsref_tolocalestring.asp + var dateFormatObj = { dateStyle: 'long' } @@ -34,6 +37,8 @@ var monthNames = [ var unavailableBefore; var unavailableAfter; + + // DATE/TIME PICKER SETTINGS END : DO NOT EDIT BELOW THIS LINE!!! var pathArray = window.location.pathname.split( '/' ); @@ -89,12 +94,98 @@ if ((datePickerFields.length>0) || (timePickerFields.length>0) || (dateTimePicke } +/* + * retrive the date format string from the locale + * the format pieces are YYYY MM DD and the delimeter + */ +function getDateFormatStringFromLocale (locale) { + const formatObj = new Intl.DateTimeFormat(locale).formatToParts(new Date()); + + return formatObj + .map(obj => { + switch (obj.type) { + case "day": + return "DD"; + case "month": + return "MM"; + case "year": + return "YYYY"; + default: + return obj.value; + } + }) + .join(""); +} + +/* + * parse a Date obj from input using the locale + * given in the global variable localeString. + * returns a date obj or 'Invalid Date' like date.parse + */ +function parseDate (input) { + var parsedDate = 'Invalid Date'; + if (input !== '') { + var format = getDateFormatStringFromLocale(localeString); + var input_parts = input.match(/(\d+)/g); + try { + var i = 0; + var format_pos = {}; + format.replace(/(YYYY|DD|MM)/g, function (part) { format_pos[part] = i++; }); + var year = parseInt(input_parts[format_pos['YYYY']]); + var month = parseInt(input_parts[format_pos['MM']]); + var day = parseInt(input_parts[format_pos['DD']]); + parsedDate = new Date(year, month - 1, day); + } catch (error) { + // in this case we return 'Invalid Date' + } + } + return parsedDate +} + +/* + * parse a Date obj with date and time from input using the locale + * given in the global variable localeString. + * returns a date obj or 'Invalid Date' like date.parse + */ +function parseDateTime(input) { + var parsedDate = 'Invalid Date'; + if (input !== '') { + var inputParts = []; + if (input.includes(" at ")) { + inputParts = input.split('at'); + } else { + inputParts[0] = input.substring(0,10); + inputParts[1] = input.substring(11); + } + console.log(inputParts); + parsedDate = parseDate(inputParts[0].trim()); + if (parsedDate == 'Invalid Date') { + return parsedDate; + } + var timeStr = inputParts[1].trim(); + var hour = 0; + var min = 0; + try { + hour = parseInt(timeStr.substring(0, 2)); + min = parseInt(timeStr.substring(3, 5)); + } catch (error) { + hour = 0; + min = 0; + } + parsedDate.setHours(hour); + parsedDate.setMinutes(min); + parsedDate.setSeconds(0); + parsedDate.setMilliseconds(0); + } + return parsedDate; +} + function formatDateObj(dateObj, outputType) { //outputType can be time, date or timedate var options = { year: 'numeric', month: '2-digit', day: '2-digit' }; if ((outputType == 'date') || (outputType == 'datetime')) { - var output = dateObj.toLocaleString('en-US', options); + var output = dateObj.toLocaleString(localeString, options); if (outputType == 'date') { return output; } @@ -109,7 +200,11 @@ function formatDateObj(dateObj, outputType) { return time; } else { //this MUST be a datetime outputType! - output+= ' at ' + time; + if (localeString == 'en-GB') { + output += ' at ' + time; + } else { + output += ' ' + time; + } return output; } } @@ -142,8 +237,7 @@ function initDateRanges() { var thisValue = dateRangeInputFields[i].value; if (thisValue.length == 10) { - var theDate = Date.parse(thisValue); - var thisDate = new Date(theDate); + parseDate(thisValue); adjustPartnerClass(elType, partnerEl, thisDate); } @@ -304,9 +398,7 @@ function createDateFromClassName(className) { //type can be 'before' or 'after' var classStr = className.replace('before-', ''); classStr = className.replace('after-', ''); - var theDate = Date.parse(classStr); - var dateObj = new Date(theDate); - return dateObj; + return parseDate(classStr); } function buildDatePickerCalendar() { @@ -416,7 +508,15 @@ function buildAndPopulateDatePickerTbl() { var i = 1; var isCurrentDay = false; var isAvailable = false; - + + var selectedDate = parseDate(targetInputValueSm); + if (selectedDate == 'Invalid Date') { + selectedDate = new Date(); + } + var selectedDateStr = selectedDate.getFullYear(); + selectedDateStr += "-"+addZeroBefore(selectedDate.getMonth()+1); + selectedDateStr += "-"+addZeroBefore(selectedDate.getDate()); + do { //create a week row calendarWeekRow = document.createElement("tr"); @@ -456,8 +556,7 @@ function buildAndPopulateDatePickerTbl() { boxMonthNum = ("0" + (assumedDate.getMonth() + 1)).slice(-2); boxYearNum = assumedDate.getFullYear(); var unseenBoxValue = boxYearNum + '-' + boxMonthNum + '-' + boxDayNumLZ; - - if (unseenBoxValue == targetInputValueSm) { + if (unseenBoxValue == selectedDateStr) { calendarTblTd.classList.add("selected-day-cell"); } } @@ -750,9 +849,7 @@ function attemptExtractYear(text) { function getDateFromInput() { value = activeEl.value; - var theDate = Date.parse(value); - var thisDate = new Date(theDate); - return thisDate; + return parseDate(value); } @@ -1170,36 +1267,15 @@ function initDateTimePickers() { unavailableBefore = ''; unavailableAfter = ''; - if (targetInputValue !== '') { - - if(targetInputValue.includes(" at ")) { - var mon = parseInt(targetInputValue.substring(0,2)); - var day = parseInt(targetInputValue.substring(3,5)); - var year = parseInt(targetInputValue.substring(6,10)); - var hour = parseInt(targetInputValue.substring(14,16)); - var min = parseInt(targetInputValue.substring(17,21)); - } else { - var mon = parseInt(targetInputValue.substring(5,7)); - var day = parseInt(targetInputValue.substring(8,10)); - var year = parseInt(targetInputValue.substring(0,4)); - var hour = parseInt(targetInputValue.substring(11,13)); - var min = parseInt(targetInputValue.substring(14,16)); - } - - var sec = 0; - assumedDate = new Date(year, mon-1, day, hour, min, sec); - - if (assumedDate == 'Invalid Date') { - assumedDate = new Date; - } - - } else { + console.log('parse Datetime'); + assumedDate = parseDateTime(targetInputValue); + if (assumedDate == 'Invalid Date') { assumedDate = new Date; } - + console.log(assumedDate); clickedTimePickerEl = ev.target; buildDateTimePickerCalendar(); }); } -} \ No newline at end of file +} From ded22ff66ae4a59d8403f37b817667922bef5a5f Mon Sep 17 00:00:00 2001 From: ReDeLe-design <72892858+ReDeLe-design@users.noreply.github.com> Date: Mon, 15 Aug 2022 10:24:27 +0200 Subject: [PATCH 011/100] Update trongate-datetime.js remove console.log() --- public/js/trongate-datetime.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/public/js/trongate-datetime.js b/public/js/trongate-datetime.js index 9c19e099..35e411cc 100755 --- a/public/js/trongate-datetime.js +++ b/public/js/trongate-datetime.js @@ -157,7 +157,6 @@ function parseDateTime(input) { inputParts[0] = input.substring(0,10); inputParts[1] = input.substring(11); } - console.log(inputParts); parsedDate = parseDate(inputParts[0].trim()); if (parsedDate == 'Invalid Date') { return parsedDate; @@ -1267,12 +1266,10 @@ function initDateTimePickers() { unavailableBefore = ''; unavailableAfter = ''; - console.log('parse Datetime'); assumedDate = parseDateTime(targetInputValue); if (assumedDate == 'Invalid Date') { assumedDate = new Date; } - console.log(assumedDate); clickedTimePickerEl = ev.target; buildDateTimePickerCalendar(); }); From 8d7fdfcd31463d1ba27f781edb9466bcdf90d211 Mon Sep 17 00:00:00 2001 From: Simon Field Date: Mon, 29 Aug 2022 20:52:39 +1000 Subject: [PATCH 012/100] Update to fix Implicit conversion Fix to remove Deprecated: Implicit conversion from float to int loses precision on $width and $height of image --- engine/Image.php | 102 +++++++++++++++++++---------------------------- 1 file changed, 40 insertions(+), 62 deletions(-) diff --git a/engine/Image.php b/engine/Image.php index 73d7a44a..4dd5fc54 100755 --- a/engine/Image.php +++ b/engine/Image.php @@ -1,6 +1,5 @@ 'image/jpeg', IMAGETYPE_GIF => 'image/gif', - IMAGETYPE_PNG =>'image/png', + IMAGETYPE_PNG => 'image/png', ]; /** * @param null $filename */ - public function __construct($filename = null) - { + public function __construct($filename = null) { if (extension_loaded('gd')) { if ($filename) { $this->fileName = $filename; @@ -34,8 +32,7 @@ public function __construct($filename = null) } } - private function checkFileExists($path) - { + private function checkFileExists($path) { if (!file_exists($path)) { throw new NotFoundException("$path does not exist"); } @@ -46,17 +43,16 @@ private function checkFileExists($path) * @param string $filename * @throws NotFoundException */ - public function load($filename) - { + public function load($filename) { $this->checkFileExists($filename); $imageInfo = getimagesize($filename); $this->imageType = $imageInfo[2]; - if( $this->imageType == IMAGETYPE_JPEG ) { + if ($this->imageType == IMAGETYPE_JPEG) { $this->image = imagecreatefromjpeg($filename); - } elseif( $this->imageType == IMAGETYPE_GIF ) { + } elseif ($this->imageType == IMAGETYPE_GIF) { $this->image = imagecreatefromgif($filename); - } elseif( $this->imageType == IMAGETYPE_PNG ) { + } elseif ($this->imageType == IMAGETYPE_PNG) { $this->image = imagecreatefrompng($filename); } } @@ -67,8 +63,7 @@ public function load($filename) * @param int $compression * @param string $permissions */ - public function save($filename = null, $compression = 100, $permissions = null) - { + public function save($filename = null, $compression = 100, $permissions = null) { $filename = ($filename) ?: $this->fileName; switch ($this->getImageType()) { @@ -84,7 +79,7 @@ public function save($filename = null, $compression = 100, $permissions = null) break; } - if( $permissions !== null) { + if ($permissions !== null) { chmod($filename, (int) $permissions); } } @@ -94,8 +89,7 @@ public function save($filename = null, $compression = 100, $permissions = null) * @param bool $return either output directly * @return null|string image contents (optional) */ - public function output($return = false) - { + public function output($return = false) { $contents = null; if ($return) { ob_start(); @@ -108,8 +102,8 @@ public function output($return = false) imagegif($this->image); break; case IMAGETYPE_PNG: - imagealphablending($this->image,true); - imagesavealpha($this->image,true); + imagealphablending($this->image, true); + imagesavealpha($this->image, true); imagepng($this->image); break; } @@ -122,69 +116,63 @@ public function output($return = false) /** * @return int */ - public function getWidth() - { + public function getWidth() { return imagesx($this->image); } /** * @return int */ - public function getHeight() - { + public function getHeight() { return imagesy($this->image); } /** * @param int $height */ - public function resizeToHeight($height) - { + public function resizeToHeight($height) { $ratio = $height / $this->getHeight(); $width = $this->getWidth() * $ratio; - $this->resize($width,$height); + $this->resize($width, $height); } /** * @param int $width */ - public function resizeToWidth($width) - { + public function resizeToWidth($width) { $ratio = $width / $this->getWidth(); $height = $this->getHeight() * $ratio; - $this->resize($width,$height); + $this->resize($width, $height); } /** * @param int $scale % */ - public function scale($scale) - { + public function scale($scale) { $width = $this->getWidth() * $scale / 100; $height = $this->getHeight() * $scale / 100; - $this->resize($width,$height); + $this->resize($width, $height); } /** * @param int $width * @param int $height */ - public function resizeAndCrop($width,$height) - { + public function resizeAndCrop($width, $height) { $targetRatio = $width / $height; $actualRatio = $this->getWidth() / $this->getHeight(); if ($targetRatio == $actualRatio) { // Scale to size - $this->resize($width,$height); + $this->resize($width, $height); } elseif ($targetRatio > $actualRatio) { // Resize to width, crop extra height $this->resizeToWidth($width); - $this->crop($width,$height); + $this->crop($width, $height); } else { // Resize to height, crop additional width $this->resizeToHeight($height); - $this->crop($width,$height); + $this->crop($width, $height); } } @@ -194,11 +182,10 @@ public function resizeAndCrop($width,$height) * @param int $width * @param int $height */ - public function resize($width,$height) - { - $newImage = imagecreatetruecolor($width, $height); + public function resize($width, $height) { + $newImage = imagecreatetruecolor((int)$width, (int)$height); - if ( ($this->getImageType() == IMAGETYPE_GIF) || ($this->getImageType() == IMAGETYPE_PNG) ) { + if (($this->getImageType() == IMAGETYPE_GIF) || ($this->getImageType() == IMAGETYPE_PNG)) { // Get transparency color's index number $transparency = imagecolortransparent($this->image); @@ -208,8 +195,7 @@ public function resize($width,$height) // deal with alpha channels $this->prepWithExistingIndex($newImage, $transparency); - - } elseif ($this->getImageType() == IMAGETYPE_PNG) { + } elseif ($this->getImageType() == IMAGETYPE_PNG) { // deal with alpha channels $this->prepTransparentPng($newImage); @@ -217,7 +203,7 @@ public function resize($width,$height) } // Now resample the image - imagecopyresampled($newImage, $this->image, 0, 0, 0, 0, $width, $height, $this->getWidth(), $this->getHeight()); + imagecopyresampled($newImage, $this->image, 0, 0, 0, 0, (int)$width, (int)$height, $this->getWidth(), $this->getHeight()); // And allocate to $this $this->image = $newImage; @@ -227,8 +213,7 @@ public function resize($width,$height) * @param $resource * @param $index */ - private function prepWithExistingIndex($resource, $index) - { + private function prepWithExistingIndex($resource, $index) { // Get the array of RGB vals for the transparency index $transparentColor = imagecolorsforindex($this->image, $index); @@ -245,8 +230,7 @@ private function prepWithExistingIndex($resource, $index) /** * @param $resource */ - private function prepTransparentPng($resource) - { + private function prepTransparentPng($resource) { // Set blending mode as false imagealphablending($resource, false); @@ -266,8 +250,7 @@ private function prepTransparentPng($resource) * @param int $height * @param string $trim */ - public function crop($width,$height, $trim = 'center') - { + public function crop($width, $height, $trim = 'center') { $offsetX = 0; $offsetY = 0; $currentWidth = $this->getWidth(); @@ -284,7 +267,7 @@ public function crop($width,$height, $trim = 'center') } } - $newImage = imagecreatetruecolor($width,$height); + $newImage = imagecreatetruecolor($width, $height); imagecopyresampled($newImage, $this->image, 0, 0, $offsetX, $offsetY, $width, $height, $width, $height); $this->image = $newImage; } @@ -292,8 +275,7 @@ public function crop($width,$height, $trim = 'center') /** * @return mixed */ - public function getImageType() - { + public function getImageType() { return $this->imageType; } @@ -301,8 +283,7 @@ public function getImageType() * @return mixed * @throws NothingLoadedException */ - public function getHeader() - { + public function getHeader() { if (!$this->imageType) { throw new NothingLoadedException(); } @@ -312,16 +293,13 @@ public function getHeader() /** * Frees up memory */ - public function destroy() - { + public function destroy() { imagedestroy($this->image); } } -class NothingLoadedException extends Exception -{ +class NothingLoadedException extends Exception { } -class NotFoundException extends Exception -{ -} \ No newline at end of file +class NotFoundException extends Exception { +} From 4fd5a93116c63910d51f010361499a16fa74d8b5 Mon Sep 17 00:00:00 2001 From: "Dr. Tobias Quathamer" Date: Mon, 29 Aug 2022 14:11:53 +0200 Subject: [PATCH 013/100] Spelling fixes --- README.md | 4 ++-- config/site_owner.php | 2 +- engine/get_segments.php | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index a635c5d1..36708f8a 100755 --- a/README.md +++ b/README.md @@ -94,7 +94,7 @@ Trongate is also the first PHP framework that has been built with a determinatio
    Graphical Query Builder

    - Trongate is the only framework that comes with a free graphical SQL query builder. So, now you can build complex table joins easily and liberate yourself from costly db mangement software. + Trongate is the only framework that comes with a free graphical SQL query builder. So, now you can build complex table joins easily and liberate yourself from costly db management software.

    @@ -130,7 +130,7 @@ Trongate is also the first PHP framework that has been built with a determinatio
    Modular HAVC

    - Trongate uses Hierarchical Asset View Controller architecture. It's like HMVC on steroids! Finally, the PHP community have a "truely modular" PHP framework. + Trongate uses Hierarchical Asset View Controller architecture. It's like HMVC on steroids! Finally, the PHP community have a "truly modular" PHP framework.

    diff --git a/config/site_owner.php b/config/site_owner.php index 53ddd300..05b035b5 100755 --- a/config/site_owner.php +++ b/config/site_owner.php @@ -1,5 +1,5 @@ $custom_route_destination) { $custom_route_segments = explode('/',$custom_route); if(count($target_segments) == count($custom_route_segments)){ - if ($custom_route == $target_segments_str) { //perfect match; return immediatly + if ($custom_route == $target_segments_str) { //perfect match; return immediately $target_url = str_replace($custom_route, $custom_route_destination, $target_url); break; } From b247133a2d82c7ec8dbd5762b0c74801cc432c59 Mon Sep 17 00:00:00 2001 From: "Dr. Tobias Quathamer" Date: Tue, 30 Aug 2022 13:39:51 +0200 Subject: [PATCH 014/100] Some more spelling fixes --- engine/get_segments.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/engine/get_segments.php b/engine/get_segments.php index 1d27c664..e21b98c1 100755 --- a/engine/get_segments.php +++ b/engine/get_segments.php @@ -1,10 +1,10 @@ 1) { From ec5c6070b8978da7221fc4e34f309d364311da38 Mon Sep 17 00:00:00 2001 From: David Connelly Date: Wed, 31 Aug 2022 11:21:14 +0100 Subject: [PATCH 015/100] A tweak to the default welcome view file. No big deal. --- modules/welcome/views/welcome.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/modules/welcome/views/welcome.php b/modules/welcome/views/welcome.php index fcac7b9f..3a6ef893 100755 --- a/modules/welcome/views/welcome.php +++ b/modules/welcome/views/welcome.php @@ -33,5 +33,4 @@ - - + \ No newline at end of file From bbcbaf7a2f67312fe647a70a3bd215ea4011053a Mon Sep 17 00:00:00 2001 From: David Connelly Date: Wed, 31 Aug 2022 11:37:18 +0100 Subject: [PATCH 016/100] Removed unnecessary / unused 'pagination' method from Trongate.php. Thank you, Avenirer from bringing this to our attention! --- engine/Trongate.php | 4 ---- 1 file changed, 4 deletions(-) diff --git a/engine/Trongate.php b/engine/Trongate.php index 10b7b5ce..b844897b 100755 --- a/engine/Trongate.php +++ b/engine/Trongate.php @@ -32,10 +32,6 @@ public function load($helper) { $this->$helper = new $helper; } - public function pagination($pagination_data) { - require_once 'tg_helpers/'.$helper.'.php'; - } - public function template($template_name, $data=NULL) { $template_controller_path = '../templates/controllers/Templates.php'; require_once $template_controller_path; From bb49cef00e2acb1048a10b92a8a788ce02f7fdf4 Mon Sep 17 00:00:00 2001 From: David Connelly Date: Wed, 31 Aug 2022 12:03:32 +0100 Subject: [PATCH 017/100] Added spinner class to Trongate CSS. Now you can have a nice little spinner on your pages by simply adding a div with a class of 'spinner'. By default, the spinner with be aligned to the center. To have the spinner aligned left, add an additional class of 'spinner-lhs'. To have the spinner aligned to the right, add an additional class of 'spinner-rhs'. --- public/css/trongate.css | 42 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/public/css/trongate.css b/public/css/trongate.css index f7867390..af757ab8 100755 --- a/public/css/trongate.css +++ b/public/css/trongate.css @@ -351,6 +351,48 @@ hr { height: 100%; } +.spinner { + display: flex; + align-items: center; + justify-content: center; + pointer-events: none; + position: relative; +} + +.spinner::after { + animation: spinning 0.5s infinite linear; + width: 24px; + height: 24px; + background: 0 0; + border: 3px solid var(--primary); + border-radius: 50%; + border-right-color: transparent; + border-top-color: transparent; + content: ""; + display: block; + opacity: 1; + padding: 0; + position: absolute; + z-index: 1; +} + +.spinner-lhs { + justify-content: flex-start; +} + +.spinner-rhs { + justify-content: flex-end; +} + +@keyframes spinning { + 0% { + transform: rotate(0); + } + 100% { + transform: rotate(360deg); + } +} + @media screen and (max-width: 550px) { button, .button { width: 100%; From 0b1f2da17a0fecf77d01768c30b79a33d8de599f Mon Sep 17 00:00:00 2001 From: David Connelly Date: Wed, 31 Aug 2022 12:19:42 +0100 Subject: [PATCH 018/100] A few improvements and tweaks to the Model class -> basically making sure update ids are integers. --- engine/Model.php | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/engine/Model.php b/engine/Model.php index 367ed90c..14cef68d 100755 --- a/engine/Model.php +++ b/engine/Model.php @@ -186,7 +186,7 @@ public function get_where_custom($column, $value, $operator='=', $order_by='id', //fetch a single record public function get_where($id, $target_tbl=NULL) { - $data['id'] = $id; + $data['id'] = (int) $id; if (!isset($target_tbl)) { $target_tbl = $this->get_table_from_url(); @@ -320,12 +320,6 @@ public function get_max($target_tbl=NULL) { } - //get result set as array of objects - public function resultSet() { - // $this->execute(); - // return $this->stmt->fetchAll(PDO::FETCH_OBJ); - } - public function show_query($query, $data, $caveat=NULL) { $keys = array(); $values = $data; @@ -438,7 +432,7 @@ public function update($update_id, $data, $target_tbl=NULL) { $sql = rtrim($sql, ', '); $sql.= " WHERE `$target_tbl`.`id` = :id"; - $data['id'] = $update_id; + $data['id'] = (int) $update_id; $data = $data; if ($this->debug == true) { @@ -456,7 +450,7 @@ public function delete($id, $target_tbl=NULL) { } $sql = "DELETE from `$target_tbl` WHERE id = :id "; - $data['id'] = $id; + $data['id'] = (int) $id; if ($this->debug == true) { $query_to_execute = $this->show_query($sql, $data, $this->query_caveat); From d382dd862f2116417e11dd59231ef0ed7692575c Mon Sep 17 00:00:00 2001 From: David Connelly Date: Thu, 1 Sep 2022 12:10:35 +0100 Subject: [PATCH 019/100] Trongate now has the ability to display in-field form validation errors! To get this working; 1). remove validation_errors(); statement (on your form page), 2). give your form a class of 'highlight-errors'. 3). For each form field where you'd like to display validation error(s), add ->. echo validation_errors('form_field_name'); If you're using in-field errors and you'd like to remove the default validation error alert, set .validation-error-alert class to a display type of 'none'. --- engine/tg_helpers/file_validation_helper.php | 20 +- engine/tg_helpers/form_helper.php | 27 ++- engine/tg_helpers/validation_helper.php | 202 ++++++++++++------- engine/views/highlight_errors.txt | 77 +++++++ public/css/trongate.css | 25 +++ 5 files changed, 265 insertions(+), 86 deletions(-) create mode 100644 engine/views/highlight_errors.txt diff --git a/engine/tg_helpers/file_validation_helper.php b/engine/tg_helpers/file_validation_helper.php index 0f09681c..87014eb8 100755 --- a/engine/tg_helpers/file_validation_helper.php +++ b/engine/tg_helpers/file_validation_helper.php @@ -8,7 +8,7 @@ $file_checks_to_run[$file_validation_test] = $rule_content; } -$target_file = get_target_file(); +$target_file = get_target_file($key); $target_file = $_FILES[$target_file]; $temp_file_name = $target_file['tmp_name']; $file_size = $target_file['size']/1000; //kilobytes) @@ -21,7 +21,7 @@ $result = check_is_allowed_type($target_file['name'], $file_check_value); if ($result !='') { - $this->form_submission_errors[] = $result; + $this->form_submission_errors[$key][] = $result; } break; @@ -29,7 +29,7 @@ $result = check_file_size($file_size, $file_check_value); if ($result !='') { - $this->form_submission_errors[] = $result; + $this->form_submission_errors[$key][] = $result; } break; case 'max_height': @@ -42,7 +42,7 @@ if ((!is_numeric($image_height)) || ($image_height>$file_check_value)) { $result = 'The file exceeds the maximum allowed height ('.$file_check_value.' pixels).'; - $this->form_submission_errors[] = $result; + $this->form_submission_errors[$key][] = $result; } break; @@ -56,7 +56,7 @@ if ((!is_numeric($image_width)) || ($image_width>$file_check_value)) { $result = 'The file exceeds the maximum allowed width ('.$file_check_value.' pixels).'; - $this->form_submission_errors[] = $result; + $this->form_submission_errors[$key][] = $result; } break; @@ -70,7 +70,7 @@ if ((!is_numeric($image_height)) || ($image_height<$file_check_value)) { $result = 'The image height falls below the minimum allowed height ('.$file_check_value.' pixels).'; - $this->form_submission_errors[] = $result; + $this->form_submission_errors[$key][] = $result; } break; @@ -84,7 +84,7 @@ if ((!is_numeric($image_width)) || ($image_width<$file_check_value)) { $result = 'The image width falls below the minimum allowed width ('.$file_check_value.' pixels).'; - $this->form_submission_errors[] = $result; + $this->form_submission_errors[$key][] = $result; } break; @@ -99,7 +99,7 @@ if ($image_width !== $image_height) { $result = 'The image width does not match the image height.'; - $this->form_submission_errors[] = $result; + $this->form_submission_errors[$key][] = $result; } break; @@ -136,7 +136,7 @@ function check_file_size($file_size, $file_check_value) { return $result; } -function get_target_file() { - $userfile = array_keys($_FILES)[0]; +function get_target_file($key) { + $userfile = array_keys($_FILES)[$key]; return $userfile; } \ No newline at end of file diff --git a/engine/tg_helpers/form_helper.php b/engine/tg_helpers/form_helper.php index 97e9f00d..78de678c 100755 --- a/engine/tg_helpers/form_helper.php +++ b/engine/tg_helpers/form_helper.php @@ -38,11 +38,34 @@ function form_close() { 'cost' => 11 )); - echo form_hidden('csrf_token', $csrf_token); - $html = ''; + $html = ''; + $html.= ''; + + if (isset($_SESSION['form_submission_errors'])) { + $errors_json = json_encode($_SESSION['form_submission_errors']); + $inline_validation_js = highlight_validation_errors($errors_json); + $html.= $inline_validation_js; + unset($_SESSION['form_submission_errors']); + } + return $html; } +function build_output_str() { + $output_str = file_get_contents(APPPATH.'engine/views/highlight_errors.txt'); + return $output_str; +} + +function highlight_validation_errors($errors_json) { + $code = '
    '; + $output_str = build_output_str(); + $code.= ''; + $code.= '
    '; + return $code; +} + function get_attributes_str($attributes) { $attributes_str = ''; if (!isset($value)) { diff --git a/engine/tg_helpers/validation_helper.php b/engine/tg_helpers/validation_helper.php index 2ed800c2..8f137be6 100755 --- a/engine/tg_helpers/validation_helper.php +++ b/engine/tg_helpers/validation_helper.php @@ -34,48 +34,47 @@ public function set_rules($key, $label, $rules) { } private function run_validation_test($validation_data, $rules=null) { - extract($validation_data); - switch ($test_to_run) { + switch ($validation_data['test_to_run']) { case 'required': - $this->check_for_required($label, $posted_value); + $this->check_for_required($validation_data); break; case 'numeric': - $this->check_for_numeric($label, $posted_value); + $this->check_for_numeric($validation_data); break; case 'integer': - $this->check_for_integer($label, $posted_value); + $this->check_for_integer($validation_data); break; case 'decimal': - $this->check_for_decimal($label, $posted_value); + $this->check_for_decimal($validation_data); break; case 'valid_email': - $this->valid_email($label, $posted_value); + $this->valid_email($validation_data); break; case 'validate_file': - $this->validate_file($key, $label, $rules); + $this->validate_file($validation_data, $rules); break; case 'valid_datepicker_us': - $this->valid_datepicker_us($label, $posted_value); + $this->valid_datepicker_us($validation_data); break; case 'valid_datepicker_eu': - $this->valid_datepicker_eu($label, $posted_value); + $this->valid_datepicker_eu($validation_data); break; case 'valid_datetimepicker_us': - $this->valid_datetimepicker_us($label, $posted_value); + $this->valid_datetimepicker_us($validation_data); break; case 'valid_datetimepicker_eu': - $this->valid_datetimepicker_eu($label, $posted_value); + $this->valid_datetimepicker_eu($validation_data); break; case 'valid_time': - $this->valid_time($label, $posted_value); + $this->valid_time($validation_data); break; case 'unique': - $inner_value = (isset($inner_value)) ? $inner_value : 0; - $this->unique($key, $label, $posted_value, $inner_value); + $inner_value = (isset($validation_data['inner_value'])) ? $inner_value : 0; + $this->unique($validation_data, $inner_value); break; default: - $this->run_special_test($key, $label, $posted_value, $test_to_run); + $this->run_special_test($validation_data); break; } @@ -162,51 +161,50 @@ private function get_tests_to_run($rules) { return $tests_to_run; } - private function check_for_required($label, $posted_value) { - - $posted_value = trim($posted_value); + private function check_for_required($validation_data) { + extract($validation_data); + $posted_value = trim($validation_data['posted_value']); if ($posted_value == '') { - $this->form_submission_errors[] = 'The '.$label.' field is required.'; + $this->form_submission_errors[$key][] = 'The '.$label.' field is required.'; } } - private function check_for_numeric($label, $posted_value) { - + private function check_for_numeric($validation_data) { + extract($validation_data); if ((!is_numeric($posted_value)) && ($posted_value !== '')) { - $this->form_submission_errors[] = 'The '.$label.' field must be numeric.'; + $this->form_submission_errors[$key][] = 'The '.$label.' field must be numeric.'; } - } - private function check_for_integer($label, $posted_value) { - + private function check_for_integer($validation_data) { + extract($validation_data); if ($posted_value !== '') { $result = ctype_digit(strval($posted_value)); if ($result == false) { - $this->form_submission_errors[] = 'The '.$label.' field must be an integer.'; + $this->form_submission_errors[$key][] = 'The '.$label.' field must be an integer.'; } } } - private function check_for_decimal($label, $posted_value) { - + private function check_for_decimal($validation_data) { + extract($validation_data); if ($posted_value !== '') { if ((float) $posted_value == floor($posted_value)) { - $this->form_submission_errors[] = 'The '.$label.' field must contain a number with a decimal.'; + $this->form_submission_errors[$key][] = 'The '.$label.' field must contain a number with a decimal.'; } } } - private function valid_datepicker_us($label, $posted_value) { - + private function valid_datepicker_us($validation_data) { + extract($validation_data); if ($posted_value !== '') { try { @@ -216,15 +214,15 @@ private function valid_datepicker_us($label, $posted_value) { } catch (Exception $e) { - $this->form_submission_errors[] = 'The '.$label.' field must contain a valid datepicker value of the format mm-dd-yyyy.'; + $this->form_submission_errors[$key][] = 'The '.$label.' field must contain a valid datepicker value of the format mm-dd-yyyy.'; } } } - private function valid_datepicker_eu($label, $posted_value) { - + private function valid_datepicker_eu($validation_data) { + extract($validation_data); if ($posted_value !== '') { $day = substr($posted_value, 0, 2); @@ -239,15 +237,15 @@ private function valid_datepicker_eu($label, $posted_value) { return true; } catch (Exception $e) { - $this->form_submission_errors[] = 'The '.$label.' field must contain a valid datepicker value of the format dd-mm-yyyy.'; + $this->form_submission_errors[$key][] = 'The '.$label.' field must contain a valid datepicker value of the format dd-mm-yyyy.'; } } } - private function valid_datetimepicker_us($label, $posted_value) { - + private function valid_datetimepicker_us($validation_data) { + extract($validation_data); if ($posted_value !== '') { try { @@ -257,15 +255,15 @@ private function valid_datetimepicker_us($label, $posted_value) { } catch (Exception $e) { - $this->form_submission_errors[] = 'The '.$label.' field must contain a valid datetime picker value.'; + $this->form_submission_errors[$key][] = 'The '.$label.' field must contain a valid datetime picker value.'; } } } - private function valid_datetimepicker_eu($label, $posted_value) { - + private function valid_datetimepicker_eu($validation_data) { + extract($validation_data); if ($posted_value !== '') { try { @@ -281,15 +279,15 @@ private function valid_datetimepicker_eu($label, $posted_value) { } catch (Exception $e) { - $this->form_submission_errors[] = 'The '.$label.' field must contain a valid datetime picker value.'; + $this->form_submission_errors[$key][] = 'The '.$label.' field must contain a valid datetime picker value.'; } } } - private function valid_time($label, $posted_value) { - + private function valid_time($validation_data) { + extract($validation_data); if ($posted_value !== '') { $got_error = true; @@ -314,14 +312,14 @@ private function valid_time($label, $posted_value) { } if ($got_error == true) { - $this->form_submission_errors[] = 'The '.$label.' field must contain a valid time value.'; + $this->form_submission_errors[$key][] = 'The '.$label.' field must contain a valid time value.'; } } } - private function matches($label, $posted_value, $target_field) { + private function matches($key, $label, $posted_value, $target_field) { $got_error = false; @@ -342,12 +340,12 @@ private function matches($label, $posted_value, $target_field) { $target_field = $this->posted_fields[$target_field]; } - $this->form_submission_errors[] = 'The '.$label.' field does not match the '.$target_field.' field.'; + $this->form_submission_errors[$key][] = 'The '.$label.' field does not match the '.$target_field.' field.'; } } - private function differs($label, $posted_value, $target_field) { + private function differs($key, $label, $posted_value, $target_field) { //returns false if form element does not differ from the one in the parameter. $got_error = false; @@ -362,7 +360,7 @@ private function differs($label, $posted_value, $target_field) { } if ($got_error == true) { - $this->form_submission_errors[] = 'The '.$label.' field must not match the '.$target_field.' field.'; + $this->form_submission_errors[$key][] = 'The '.$label.' field must not match the '.$target_field.' field.'; } } @@ -370,7 +368,7 @@ private function differs($label, $posted_value, $target_field) { private function min_length($key, $label, $posted_value, $inner_value) { if ((strlen($_POST[$key]) < $inner_value) && ($posted_value !== '')) { - $this->form_submission_errors[] = 'The ' . $label . ' field must be at least ' . $inner_value . ' characters in length.'; + $this->form_submission_errors[$key][] = 'The ' . $label . ' field must be at least ' . $inner_value . ' characters in length.'; } } @@ -378,7 +376,7 @@ private function min_length($key, $label, $posted_value, $inner_value) { private function max_length($key, $label, $posted_value, $inner_value) { if ((strlen($_POST[$key]) > $inner_value) && ($posted_value !== '')) { - $this->form_submission_errors[] = 'The ' . $label . ' field must be no more than ' . $inner_value . ' characters in length.'; + $this->form_submission_errors[$key][] = 'The ' . $label . ' field must be no more than ' . $inner_value . ' characters in length.'; } } @@ -409,7 +407,7 @@ private function unique($key, $label, $posted_value, $inner_value=null) { $row_id = $row->id; $row_target_value = $row->$key; if (($row->id !== $allowed_id) && ($row->$key == $posted_value)) { - $this->form_submission_errors[] = 'The ' . $label . ' that you submitted is already on our system.'; + $this->form_submission_errors[$key][] = 'The ' . $label . ' that you submitted is already on our system.'; break; } } @@ -419,7 +417,7 @@ private function unique($key, $label, $posted_value, $inner_value=null) { private function greater_than($key, $label, $posted_value, $inner_value) { if (((is_numeric($_POST[$key])) && ($_POST[$key]<=$inner_value)) && ($posted_value !== '')) { - $this->form_submission_errors[] = 'The '.$label.' field must greater than '.$inner_value; + $this->form_submission_errors[$key][] = 'The '.$label.' field must greater than '.$inner_value.'.'; } } @@ -427,15 +425,15 @@ private function greater_than($key, $label, $posted_value, $inner_value) { private function less_than($key, $label, $posted_value, $inner_value) { if (((is_numeric($_POST[$key])) && ($_POST[$key]>=$inner_value)) && ($posted_value !== '')) { - $this->form_submission_errors[] = 'The '.$label.' field must less than '.$inner_value; + $this->form_submission_errors[$key][] = 'The '.$label.' field must less than '.$inner_value.'.'; } } - private function valid_email($label, $posted_value) { - + private function valid_email($validation_data) { + extract($validation_data); if ((!filter_var($posted_value, FILTER_VALIDATE_EMAIL)) && ($posted_value !== '')) { - $this->form_submission_errors[] = 'The '.$label.' field must contain a valid email address.'; + $this->form_submission_errors[$key][] = 'The '.$label.' field must contain a valid email address.'; } } @@ -450,13 +448,13 @@ private function exact_length($key, $label, $posted_value, $inner_value) { $error_msg = str_replace('characters in length.', 'character in length.', $error_msg); } - $this->form_submission_errors[] = $error_msg; + $this->form_submission_errors[$key][] = $error_msg; } } - private function run_special_test($key, $label, $posted_value, $test_to_run) { - + private function run_special_test($validation_data) { + extract($validation_data); $pos = strpos($test_to_run, '['); if (is_numeric($pos)) { @@ -472,10 +470,10 @@ private function run_special_test($key, $label, $posted_value, $test_to_run) { switch ($test_name) { case 'matches': - $this->matches($label, $posted_value, $inner_value); + $this->matches($key, $label, $posted_value, $inner_value); break; case 'differs': - $this->differs($label, $posted_value, $inner_value); + $this->differs($key, $label, $posted_value, $inner_value); break; case 'min_length': $this->min_length($key, $label, $posted_value, $inner_value); @@ -520,9 +518,7 @@ private function _get_test_name($test_to_run) { } private function validate_file($key, $label, $rules) { - require_once('file_validation_helper.php'); - } private function attempt_invoke_callback($key, $label, $posted_value, $test_to_run) { @@ -577,22 +573,80 @@ public function url_segment($num) { } -function validation_errors($opening_html=NULL, $closing_html=NULL) { - +function validation_errorsNEW($opening_html=NULL, $closing_html=NULL) { if (isset($_SESSION['form_submission_errors'])) { $form_submission_errors = $_SESSION['form_submission_errors']; + $closing_html = (isset($closing_html)) ? $closing_html : false; - if (!isset($opening_html)) { - $opening_html = '

    '; - $closing_html = '

    '; - } + if ((isset($opening_html)) && (gettype($closing_html == 'boolean'))) { + //build individual form field validation error(s) - foreach($form_submission_errors as $form_submission_error) { - echo $opening_html.$form_submission_error.$closing_html; - } + if (isset($form_submission_errors[$opening_html])) { + echo '
    '; + echo 'you got this'; - unset($_SESSION['form_submission_errors']); + } + json($form_submission_errors); + + $validation_err_str = $opening_html; + } else { + //normal error reporting + if (!isset($opening_html)) { + $opening_html = '

    '; + $closing_html = '

    '; + } + + foreach($form_submission_errors as $form_submission_error) { + $validation_err_str.= $opening_html.$form_submission_error.$closing_html; + } + + unset($_SESSION['form_submission_errors']); + } + + return $validation_err_str; } +} + + +function validation_errors($opening_html=NULL, $closing_html=NULL) { + if (isset($_SESSION['form_submission_errors'])) { + $validation_err_str = ''; + $validation_errors = []; + $closing_html = (isset($closing_html)) ? $closing_html : false; + $form_submission_errors = $_SESSION['form_submission_errors']; + + if ((isset($opening_html)) && (gettype($closing_html == 'boolean'))) { + //build individual form field validation error(s) + if (isset($form_submission_errors[$opening_html])) { + $validation_err_str.= '
    '; + $form_field_errors = $form_submission_errors[$opening_html]; + foreach($form_field_errors as $validation_error) { + $validation_err_str.= '
    ● '.$validation_error.'
    '; + } + $validation_err_str.= '
    '; + } + + } else { + //normal error reporting + foreach($form_submission_errors as $key => $form_field_errors) { + foreach($form_field_errors as $form_field_error) { + $validation_errors[] = $form_field_error; + } + } + + if (!isset($opening_html)) { + $opening_html = '

    '; + $closing_html = '

    '; + } + + foreach($validation_errors as $form_submission_error) { + $validation_err_str.= $opening_html.$form_submission_error.$closing_html; + } + + unset($_SESSION['form_submission_errors']); + } + return $validation_err_str; + } } \ No newline at end of file diff --git a/engine/views/highlight_errors.txt b/engine/views/highlight_errors.txt new file mode 100644 index 00000000..f13fffa8 --- /dev/null +++ b/engine/views/highlight_errors.txt @@ -0,0 +1,77 @@ +window.addEventListener('load', (ev) => { + let inlineValidationForms = document.getElementsByClassName('highlight-errors'); + let validationErrors = JSON.parse(validationErrorsJson); + + if (inlineValidationForms.length>0) { + for (let i = 0; i < inlineValidationForms.length; i++) { + attemptHighlightErrorFields(inlineValidationForms[i], validationErrors); + drawValidationErrorsAlert(inlineValidationForms[i]); + } + } + destroyInlineValidationBuilder(); +}); + +function drawValidationErrorsAlert(targetForm) { + let alertDiv = document.createElement('div'); + alertDiv.classList.add('validation-error-alert'); + alertDiv.classList.add('form-field-validation-error'); + let alertHeadline = document.createElement('h3'); + let gotFontAwesome = findCss('font-awesome'); + let iconCode = ' '; + alertHeadline.innerHTML = (gotFontAwesome == true) ? iconCode : ''; + alertHeadline.innerHTML+= 'Ooops! There was a problem.'; + + let infoPara = document.createElement('p'); + let infoParaText = document.createTextNode('You\'ll find more details highlighted below.'); + infoPara.appendChild(infoParaText); + + alertDiv.appendChild(alertHeadline); + alertDiv.appendChild(infoPara); + targetForm.prepend(alertDiv); +} + +function findCss(fileName) { + var finderRe = new RegExp(fileName + '.*?\.css', "i"); + var linkElems = document.getElementsByTagName("link"); + for (var i = 0, il = linkElems.length; i < il; i++) { + if (linkElems[i].href && finderRe.test(linkElems[i].href)) { + return true; + } + } + return false; +} + +function attemptHighlightErrorFields(targetForm, validationErrors) { + let allFormFields = targetForm.elements; + for (const [key, value] of Object.entries(validationErrors)) { + addErrorClasses(key, allFormFields); + } +} + +function addErrorClasses(key, allFormFields) { + for (var i = 0; i < allFormFields.length; i++) { + if (allFormFields[i]['name'] == key) { + let formFieldType = allFormFields[i]['type']; + if ((formFieldType == 'checkbox') || (formFieldType == 'radio')) { + let parentContainer = allFormFields[i].closest('div'); + parentContainer.classList.add('form-field-validation-error'); + parentContainer.style.textIndent = '7px'; + + let previousSibling = parentContainer.previousSibling; + if (previousSibling.classList.contains('validation-error-report')) { + previousSibling.style.marginTop = '21px'; + } + + } else { + allFormFields[i].classList.add('form-field-validation-error'); + } + } + } +} + +function destroyInlineValidationBuilder() { + let inlineValidationBuilders = document.getElementsByClassName('inline-validation-builder'); + for (let i = 0; i < inlineValidationBuilders.length; i++) { + inlineValidationBuilders[i].remove(); + } +} \ No newline at end of file diff --git a/public/css/trongate.css b/public/css/trongate.css index af757ab8..3da7e5ea 100755 --- a/public/css/trongate.css +++ b/public/css/trongate.css @@ -351,6 +351,31 @@ hr { height: 100%; } +.form-field-validation-error { + border: 3px #ff0000 solid !important; + background-color: #ff000011 !important; +} + +.validation-error-alert { + padding: 12px 12px 0 12px; +} + +.validation-error-alert h3 { + margin-top: 0; + display: flex; + align-items: center; + color: #ff0000; +} + +.validation-error-report { + color: #ff0000 !important; +} + +.validation-error-report > div { + margin-bottom: 7px; + font-size: 16px; +} + .spinner { display: flex; align-items: center; From 8dfd0e6db3203ae4ffb151c76b1e88e48a3ef733 Mon Sep 17 00:00:00 2001 From: David Connelly Date: Thu, 1 Sep 2022 12:31:16 +0100 Subject: [PATCH 020/100] Changed GitHub URL for Trongate framework repo. --- README.md | 28 ++++++++++++++-------------- config/config.php | 2 +- public/css/trongate.css | 1 + 3 files changed, 16 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 36708f8a..fcb498cb 100755 --- a/README.md +++ b/README.md @@ -10,20 +10,20 @@

    - - contributors + + contributors - last update + last update - - forks + + forks - - stars + + stars - - open issues + + open issues

    @@ -32,9 +32,9 @@ · Documentation · - Report Bug + Report Bug · - Request Feature + Request Feature
    @@ -161,7 +161,7 @@ Trongate is also the first PHP framework that has been built with a determinatio The Trongate PHP framework is available from GitHub at the following URL: -https://github.com/davidjconnelly/trongate-framework. +https://github.com/trongate/trongate-framework. However, the fastest and easiest way to get the Trongate PHP framework is by using the Trongate Desktop App. The Trongate Desktop App is 100% free of charge (it always will be!) and is available for Mac, Windows and Linux. The Trongate Desktop App can be downloaded from: @@ -191,8 +191,8 @@ There are a wide variety of benefits from using the Trongate Desktop App. These ## :wave: Contributing - - + + diff --git a/config/config.php b/config/config.php index be3e075f..53287914 100755 --- a/config/config.php +++ b/config/config.php @@ -10,7 +10,7 @@ Help us to achieve our goal and together we SHALL make PHP great again! The GitHub URL for Trongate is: - https://github.com/davidjconnelly/trongate-framework + https://github.com/trongate/trongate-framework Thank you and may the code be with you! - David Connelly (founder) */ diff --git a/public/css/trongate.css b/public/css/trongate.css index 3da7e5ea..5829cfb7 100755 --- a/public/css/trongate.css +++ b/public/css/trongate.css @@ -358,6 +358,7 @@ hr { .validation-error-alert { padding: 12px 12px 0 12px; + border-radius: 12px; } .validation-error-alert h3 { From f6fc1ecdecbc7c3d07d78176722f7d0398bb7a4a Mon Sep 17 00:00:00 2001 From: David Connelly Date: Tue, 6 Sep 2022 11:38:19 +0100 Subject: [PATCH 021/100] Correction to validation helper callbacks. Correction to 'public' template ('floating footer' issue). --- engine/license.txt | 2 +- engine/tg_helpers/validation_helper.php | 2 +- license.txt | 2 +- public/css/app.css | 4 ++++ 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/engine/license.txt b/engine/license.txt index bbb06682..ea0b3ce9 100755 --- a/engine/license.txt +++ b/engine/license.txt @@ -3,7 +3,7 @@ * * An open source PHP framework for web developers who like to break the rules * - * Version: 1.3.3035 + * Version: 1.3.3036 * * This product is released under the MIT License (MIT) * diff --git a/engine/tg_helpers/validation_helper.php b/engine/tg_helpers/validation_helper.php index 8f137be6..76154ca1 100755 --- a/engine/tg_helpers/validation_helper.php +++ b/engine/tg_helpers/validation_helper.php @@ -550,7 +550,7 @@ private function attempt_invoke_callback($key, $label, $posted_value, $test_to_r if (gettype($outcome) == 'string') { $outcome = str_replace('{label}', $label, $outcome); - $this->form_submission_errors[] = $outcome; + $this->form_submission_errors[$key][] = $outcome; } } diff --git a/license.txt b/license.txt index bbb06682..ea0b3ce9 100755 --- a/license.txt +++ b/license.txt @@ -3,7 +3,7 @@ * * An open source PHP framework for web developers who like to break the rules * - * Version: 1.3.3035 + * Version: 1.3.3036 * * This product is released under the MIT License (MIT) * diff --git a/public/css/app.css b/public/css/app.css index 4db0f108..29e2c145 100755 --- a/public/css/app.css +++ b/public/css/app.css @@ -71,6 +71,10 @@ header .fa { display: none; } +.wrapper { + flex-grow: 1; +} + main { width: 100%; margin: 0 auto; From 87524a4c04ce19909f66bbb10965f23813fd28c0 Mon Sep 17 00:00:00 2001 From: "Dr. Tobias Quathamer" Date: Thu, 8 Sep 2022 21:17:15 +0200 Subject: [PATCH 022/100] Fix error from rewrite for form field validation. Originally, $inner_value has been set by using extract() on the $validation_data array. Now, the extract() method call is removed, so $inner_value is undefined. --- engine/tg_helpers/validation_helper.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/engine/tg_helpers/validation_helper.php b/engine/tg_helpers/validation_helper.php index 76154ca1..94da3fe1 100755 --- a/engine/tg_helpers/validation_helper.php +++ b/engine/tg_helpers/validation_helper.php @@ -70,7 +70,7 @@ private function run_validation_test($validation_data, $rules=null) { $this->valid_time($validation_data); break; case 'unique': - $inner_value = (isset($validation_data['inner_value'])) ? $inner_value : 0; + $inner_value = $validation_data['inner_value'] ?? 0; $this->unique($validation_data, $inner_value); break; default: @@ -649,4 +649,4 @@ function validation_errors($opening_html=NULL, $closing_html=NULL) { } return $validation_err_str; } -} \ No newline at end of file +} From 3de585bc66ee190287b4d48925040bfaa20f8c81 Mon Sep 17 00:00:00 2001 From: horseman <72892858+ReDeLe-design@users.noreply.github.com> Date: Fri, 9 Sep 2022 00:17:52 +0200 Subject: [PATCH 023/100] Errors with more than one Date-Picker / Clear Date Changed the function "disableDatePickerInputs". The Line 670: var targetEl = datePickerInputs[i]; was responsible for the fact that targetEl was always pointing to the last Date-Input in a form. This was the main problem solved by using "ev.target" instead inside the event handler. Next problem was changing focus between two Date-Inout-Fields. The "mousedown" event of the next field occurs bevore the "blure" event of the old field. That leads to storing the wrong value in the variable "originalValue". This was solved bei changing "mousedown" to "focus" event. Inside the blur event I am using now the new parseDate function to test for a valid Date. The last Change was to have the possibility to clear a date input, for example in case of an optional field. Solved by testing for the "Backspace" or "Delete" key in combination with testing for "ev.target.value.length == 0". So selecting the whole date-string and press Backspace works perfekt to clear a once selected Date. --- public/js/trongate-datetime.js | 49 ++++++++++++---------------------- 1 file changed, 17 insertions(+), 32 deletions(-) diff --git a/public/js/trongate-datetime.js b/public/js/trongate-datetime.js index 35e411cc..b3422b33 100755 --- a/public/js/trongate-datetime.js +++ b/public/js/trongate-datetime.js @@ -765,54 +765,39 @@ function disableDatePickerInputs(className) { var originalValue; for (var i = 0; i < datePickerInputs.length; i++) { - var targetEl = datePickerInputs[i]; - var pressedKey; - // javascript get character that was pressed var originalValue = ''; - datePickerInputs[i].addEventListener("mousedown", (ev) => { - originalValue = targetEl.value; + datePickerInputs[i].addEventListener("focus", (ev) => { + originalValue = ev.target.value; }); - + datePickerInputs[i].addEventListener("blur", (ev) => { - - var isNumber = /^[0-9]$/i.test(pressedKey); - - if (isNumber !== true) { - targetEl.value = originalValue; - } else { - //attempt to extract the year from the form input field - var extractedYear = attemptExtractYear(targetEl.value); - - if (extractedYear !== false) { - //we have a valid year in the form input field - assumedDate.setYear(extractedYear); - } - - } - + if (parseDate(ev.target.value) == 'Invalid Date') { + ev.target.value = originalValue; + } }); + datePickerInputs[i].addEventListener("keyup", (ev) => { - pressedKey = ev.key; - - var isNumber = /^[0-9]$/i.test(pressedKey); - - if (isNumber !== true) { - targetEl.value = originalValue; + var isNumber = /^[0-9]$/i.test(ev.key); + if ((ev.key == 'Backspace') || (ev.key == 'Delete') { + if (ev.target.value.length == 0) { + originalValue = ev.target.value; + } else { + ev.target.value = originalValue; + } + } else if (isNumber !== true) { + ev.target.value = originalValue; } else { - //attempt to extract the year from the form input field - var extractedYear = attemptExtractYear(targetEl.value); + var extractedYear = attemptExtractYear(ev.target.value); if (extractedYear !== false) { //we have a valid year in the form input field assumedDate.setYear(extractedYear); activePopUp.remove(); } - - } }); From fbdefe398b9ae81dc8000e91552280be0c985904 Mon Sep 17 00:00:00 2001 From: horseman <72892858+ReDeLe-design@users.noreply.github.com> Date: Fri, 9 Sep 2022 00:24:44 +0200 Subject: [PATCH 024/100] typo --- public/js/trongate-datetime.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/js/trongate-datetime.js b/public/js/trongate-datetime.js index b3422b33..13cae7bb 100755 --- a/public/js/trongate-datetime.js +++ b/public/js/trongate-datetime.js @@ -781,7 +781,7 @@ function disableDatePickerInputs(className) { datePickerInputs[i].addEventListener("keyup", (ev) => { var isNumber = /^[0-9]$/i.test(ev.key); - if ((ev.key == 'Backspace') || (ev.key == 'Delete') { + if ((ev.key == 'Backspace') || (ev.key == 'Delete')) { if (ev.target.value.length == 0) { originalValue = ev.target.value; } else { From abe61bee65682378eb047634e4f7b6569dacae78 Mon Sep 17 00:00:00 2001 From: David Connelly Date: Fri, 9 Sep 2022 18:43:31 +0100 Subject: [PATCH 025/100] Correction to validation helper. --- engine/tg_helpers/validation_helper.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/engine/tg_helpers/validation_helper.php b/engine/tg_helpers/validation_helper.php index 76154ca1..10d85626 100755 --- a/engine/tg_helpers/validation_helper.php +++ b/engine/tg_helpers/validation_helper.php @@ -628,6 +628,8 @@ function validation_errors($opening_html=NULL, $closing_html=NULL) { $validation_err_str.= ''; } + return $validation_err_str; + } else { //normal error reporting foreach($form_submission_errors as $key => $form_field_errors) { @@ -647,6 +649,6 @@ function validation_errors($opening_html=NULL, $closing_html=NULL) { unset($_SESSION['form_submission_errors']); } - return $validation_err_str; + echo $validation_err_str; } } \ No newline at end of file From 42669b48faf53b1bcd2ee3ac860cd156f30f5189 Mon Sep 17 00:00:00 2001 From: David Connelly Date: Mon, 19 Sep 2022 00:41:13 +0100 Subject: [PATCH 026/100] added dynamic properties trait in preparation for PHP 8.2 --- engine/Dynamic_properties.php | 12 ++++++++++++ engine/Trongate.php | 2 ++ 2 files changed, 14 insertions(+) create mode 100644 engine/Dynamic_properties.php diff --git a/engine/Dynamic_properties.php b/engine/Dynamic_properties.php new file mode 100644 index 00000000..00984e6e --- /dev/null +++ b/engine/Dynamic_properties.php @@ -0,0 +1,12 @@ +attributes[$key] = $value; + } + + public function __get(string $key) { + return $this->attributes[$key]; + } +} \ No newline at end of file diff --git a/engine/Trongate.php b/engine/Trongate.php index b844897b..ed41afdb 100755 --- a/engine/Trongate.php +++ b/engine/Trongate.php @@ -1,6 +1,8 @@ Date: Thu, 22 Sep 2022 10:46:42 +0100 Subject: [PATCH 027/100] CSS Tweaks: Added 'blink' class to Trongate CSS and also added a 0.6em margin to our (loading) spinner class. --- public/css/trongate.css | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/public/css/trongate.css b/public/css/trongate.css index 5829cfb7..8e74f28a 100755 --- a/public/css/trongate.css +++ b/public/css/trongate.css @@ -383,6 +383,7 @@ hr { justify-content: center; pointer-events: none; position: relative; + margin: 0.6em; } .spinner::after { @@ -419,6 +420,20 @@ hr { } } +.blink{ + text-decoration: blink; + -webkit-animation-name: blinker; + -webkit-animation-duration: 0.5s; + -webkit-animation-iteration-count:infinite; + -webkit-animation-timing-function:ease-in-out; + -webkit-animation-direction: alternate; +} + +@-webkit-keyframes blinker { + from {opacity: 1.0;} + to {opacity: 0.0;} +} + @media screen and (max-width: 550px) { button, .button { width: 100%; From 142e908c0100386c2cd691eb4becff75e01e3843 Mon Sep 17 00:00:00 2001 From: Simon Field Date: Wed, 28 Sep 2022 00:28:22 +1000 Subject: [PATCH 028/100] fix deprecation and add non-Latin characters --- engine/tg_helpers/url.php | 80 +++++++++++++++++++++------------------ 1 file changed, 43 insertions(+), 37 deletions(-) diff --git a/engine/tg_helpers/url.php b/engine/tg_helpers/url.php index 956d05d8..d6f9854e 100755 --- a/engine/tg_helpers/url.php +++ b/engine/tg_helpers/url.php @@ -1,5 +1,5 @@ $value) { - $extra.= ' '.$key.'="'.$value.'"'; + $extra .= ' ' . $key . '="' . $value . '"'; } } if (isset($additional_code)) { - $extra.= ' '.$additional_code; + $extra .= ' ' . $additional_code; } - $link = ''.$text.''; + $link = '' . $text . ''; return $link; } @@ -75,14 +75,28 @@ function nice_price($num) { return $nice_price; } -function url_title($str, $make_lowercase=false) { - $str = $make_lowercase == true ? trim(strtolower($str)) : trim($str); - $str = preg_replace('/\s+/', ' ', $str); - $str = preg_replace("/[^A-Za-z0-9 _]/", '', $str); - $str = rawurlencode(utf8_encode($str)); - $str = preg_replace('/-+/', '-', $str); - $str = str_replace("%20", '-', $str); - return $str; +/** + * It takes a string, converts it to lowercase, replaces all non-alphanumeric characters with a dash, + * and trims any leading or trailing dashes. + * + * @author Special thanks to framex who posted this fix on the help-bar + * @see https://trongate.io/help_bar/thread/h7W9QyPcsx69 + * + * @param value The string to be converted. + * @param transliteration If you want to transliterate the string, set this to true. + * + * @return The slugified version of the string. + */ +function url_title($value, $transliteration = true) { + if (extension_loaded('intl') && $transliteration == true) { + $transliterator = \Transliterator::create('Any-Latin; Latin-ASCII'); + $value = $transliterator->transliterate($value); + } + $slug = html_entity_decode($value, ENT_QUOTES, 'UTF-8'); + $slug = preg_replace('~[^\pL\d]+~u', '-', $slug); + $slug = trim($slug, '-'); + $slug = strtolower($slug); + return $slug; } function api_auth() { @@ -94,14 +108,14 @@ function api_auth() { if ((isset($segments[0])) && (isset($segments[1]))) { $current_module_bits = explode('-', $segments[0]); $current_module = $current_module_bits[0]; - $filepath = APPPATH.'modules/'.$current_module.'/assets/api.json'; + $filepath = APPPATH . 'modules/' . $current_module . '/assets/api.json'; if (file_exists($filepath)) { - + //extract the rules for the current path $target_method = $segments[1]; $settings = file_get_contents($filepath); - $endpoints = json_decode($settings, true); + $endpoints = json_decode($settings, true); $current_uri_path = str_replace(BASE_URL, '', current_url()); $current_uri_bits = explode('/', $current_uri_path); @@ -122,19 +136,16 @@ function api_auth() { if (!is_numeric(strpos($value, '{'))) { $required_segments[$key] = $value; } - } foreach ($current_uri_bits as $key => $value) { - + if (isset($required_segments[$key])) { if ($value !== $required_segments[$key]) { $segments_match = false; } - } - } if ($segments_match == true) { @@ -143,7 +154,7 @@ function api_auth() { $token_validation_data['module_name'] = $current_module; $token_validation_data['module_endpoints'] = $endpoints; - $api_class_location = APPPATH.'engine/Api.php'; + $api_class_location = APPPATH . 'engine/Api.php'; if (file_exists($api_class_location)) { include_once $api_class_location; @@ -151,29 +162,24 @@ function api_auth() { $api_helper->_validate_token($token_validation_data); $validation_complete = true; } - } if (isset($required_segments)) { unset($required_segments); } - } - } - } - } if ($validation_complete == false) { http_response_code(401); - echo "Invalid token."; die(); + echo "Invalid token."; + die(); } - } -function make_rand_str($strlen, $uppercase=false) { +function make_rand_str($strlen, $uppercase = false) { $characters = '23456789abcdefghjkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ'; $random_string = ''; for ($i = 0; $i < $strlen; $i++) { @@ -186,10 +192,10 @@ function make_rand_str($strlen, $uppercase=false) { return $random_string; } -function json($data, $kill_script=null) { - echo '
    '.json_encode($data, JSON_PRETTY_PRINT).'
    '; +function json($data, $kill_script = null) { + echo '
    ' . json_encode($data, JSON_PRETTY_PRINT) . '
    '; if (isset($kill_script)) { die(); } -} \ No newline at end of file +} From 2ac86eed579202c72d5dce58ae8ec9c95a152091 Mon Sep 17 00:00:00 2001 From: Simon Field Date: Mon, 3 Oct 2022 09:21:07 +0000 Subject: [PATCH 029/100] 3 Oct 2022, 8:21 pm --- modules/doco/assets/api.json | 134 ++++++++++++++++++++++++++++++ modules/doco/controllers/Doco.php | 6 ++ 2 files changed, 140 insertions(+) create mode 100644 modules/doco/assets/api.json create mode 100644 modules/doco/controllers/Doco.php diff --git a/modules/doco/assets/api.json b/modules/doco/assets/api.json new file mode 100644 index 00000000..6dc09174 --- /dev/null +++ b/modules/doco/assets/api.json @@ -0,0 +1,134 @@ +{ + "Remember Positions": { + "url_segments": "doco/remember_positions", + "request_type": "POST", + "description": "Remember positions of some child nodes", + "enableParams": true, + "authorization":{ + "roles": [ + "admin" + ] + } + }, + "Get": { + "url_segments": "api/get/doco", + "request_type": "GET", + "description": "Fetch rows from table", + "enableParams": true, + "authorization":{ + "roles": [ + "admin" + ] + } + }, + "Get By Post": { + "url_segments": "api/get/doco", + "request_type": "POST", + "description": "Fetch rows from table using POST request.", + "enableParams": true, + "authorization":{ + "roles": [ + "admin" + ] + } + }, + "Find One": { + "url_segments": "api/get/doco/{id}", + "request_type": "GET", + "description": "Fetch one row", + "required_fields": [ + { + "name": "id", + "label": "ID" + } + ] + }, + "Exists": { + "url_segments": "api/exists/doco/{id}", + "request_type": "GET", + "description": "Check if instance exists", + "required_fields": [ + { + "name": "id", + "label": "ID" + } + ] + }, + "Count": { + "url_segments": "api/count/doco", + "request_type": "GET", + "description": "Count number of records", + "enableParams": true + }, + "Count By Post": { + "url_segments": "api/count/doco", + "request_type": "POST", + "description": "Count number of records using POST request", + "enableParams": true, + "authorization":{ + "roles": [ + "admin" + ] + } + }, + "Create": { + "url_segments": "api/create/doco", + "request_type": "POST", + "description": "Insert database record", + "enableParams": true, + "authorization":{ + "roles": [ + "admin" + ] + }, + "beforeHook": "_prep_input", + "afterHook": "_fetch_item_details" + }, + "Insert Batch": { + "url_segments": "api/batch/doco", + "request_type": "POST", + "description": "Insert multiple records", + "enableParams": true + }, + "Update": { + "url_segments": "api/update/doco/{id}", + "request_type": "PUT", + "description": "Update a database record", + "enableParams": true, + "required_fields": [ + { + "name": "id", + "label": "ID" + } + ], + "authorization":{ + "roles": [ + "admin" + ] + }, + "beforeHook": "_prep_input", + "afterHook": "_fetch_item_details" + }, + "Destroy": { + "url_segments": "api/destroy/doco", + "request_type": "DELETE", + "description": "Delete row or rows", + "enableParams": true + }, + "Delete One": { + "url_segments": "api/delete/doco/{id}", + "request_type": "DELETE", + "description": "Delete one row", + "required_fields": [ + { + "name": "id", + "label": "ID" + } + ], + "authorization":{ + "roles": [ + "admin" + ] + } + } +} \ No newline at end of file diff --git a/modules/doco/controllers/Doco.php b/modules/doco/controllers/Doco.php new file mode 100644 index 00000000..f9327016 --- /dev/null +++ b/modules/doco/controllers/Doco.php @@ -0,0 +1,6 @@ + Date: Tue, 4 Oct 2022 00:02:18 +0000 Subject: [PATCH 030/100] 4 Oct 2022, 11:02 am --- modules/doco/assets/api.json | 134 ------------------------------ modules/doco/controllers/Doco.php | 6 -- 2 files changed, 140 deletions(-) delete mode 100644 modules/doco/assets/api.json delete mode 100644 modules/doco/controllers/Doco.php diff --git a/modules/doco/assets/api.json b/modules/doco/assets/api.json deleted file mode 100644 index 6dc09174..00000000 --- a/modules/doco/assets/api.json +++ /dev/null @@ -1,134 +0,0 @@ -{ - "Remember Positions": { - "url_segments": "doco/remember_positions", - "request_type": "POST", - "description": "Remember positions of some child nodes", - "enableParams": true, - "authorization":{ - "roles": [ - "admin" - ] - } - }, - "Get": { - "url_segments": "api/get/doco", - "request_type": "GET", - "description": "Fetch rows from table", - "enableParams": true, - "authorization":{ - "roles": [ - "admin" - ] - } - }, - "Get By Post": { - "url_segments": "api/get/doco", - "request_type": "POST", - "description": "Fetch rows from table using POST request.", - "enableParams": true, - "authorization":{ - "roles": [ - "admin" - ] - } - }, - "Find One": { - "url_segments": "api/get/doco/{id}", - "request_type": "GET", - "description": "Fetch one row", - "required_fields": [ - { - "name": "id", - "label": "ID" - } - ] - }, - "Exists": { - "url_segments": "api/exists/doco/{id}", - "request_type": "GET", - "description": "Check if instance exists", - "required_fields": [ - { - "name": "id", - "label": "ID" - } - ] - }, - "Count": { - "url_segments": "api/count/doco", - "request_type": "GET", - "description": "Count number of records", - "enableParams": true - }, - "Count By Post": { - "url_segments": "api/count/doco", - "request_type": "POST", - "description": "Count number of records using POST request", - "enableParams": true, - "authorization":{ - "roles": [ - "admin" - ] - } - }, - "Create": { - "url_segments": "api/create/doco", - "request_type": "POST", - "description": "Insert database record", - "enableParams": true, - "authorization":{ - "roles": [ - "admin" - ] - }, - "beforeHook": "_prep_input", - "afterHook": "_fetch_item_details" - }, - "Insert Batch": { - "url_segments": "api/batch/doco", - "request_type": "POST", - "description": "Insert multiple records", - "enableParams": true - }, - "Update": { - "url_segments": "api/update/doco/{id}", - "request_type": "PUT", - "description": "Update a database record", - "enableParams": true, - "required_fields": [ - { - "name": "id", - "label": "ID" - } - ], - "authorization":{ - "roles": [ - "admin" - ] - }, - "beforeHook": "_prep_input", - "afterHook": "_fetch_item_details" - }, - "Destroy": { - "url_segments": "api/destroy/doco", - "request_type": "DELETE", - "description": "Delete row or rows", - "enableParams": true - }, - "Delete One": { - "url_segments": "api/delete/doco/{id}", - "request_type": "DELETE", - "description": "Delete one row", - "required_fields": [ - { - "name": "id", - "label": "ID" - } - ], - "authorization":{ - "roles": [ - "admin" - ] - } - } -} \ No newline at end of file diff --git a/modules/doco/controllers/Doco.php b/modules/doco/controllers/Doco.php deleted file mode 100644 index f9327016..00000000 --- a/modules/doco/controllers/Doco.php +++ /dev/null @@ -1,6 +0,0 @@ - Date: Tue, 4 Oct 2022 00:03:49 +0000 Subject: [PATCH 031/100] 4 Oct 2022, 11:03 am --- engine/tg_helpers/url.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/engine/tg_helpers/url.php b/engine/tg_helpers/url.php index d6f9854e..7ebd2c7f 100755 --- a/engine/tg_helpers/url.php +++ b/engine/tg_helpers/url.php @@ -82,10 +82,10 @@ function nice_price($num) { * @author Special thanks to framex who posted this fix on the help-bar * @see https://trongate.io/help_bar/thread/h7W9QyPcsx69 * - * @param value The string to be converted. - * @param transliteration If you want to transliterate the string, set this to true. + * @param string value The string to be converted. + * @param bool transliteration If you want to transliterate the string, set this to true. * - * @return The slugified version of the string. + * @return string The slugified version of the string. */ function url_title($value, $transliteration = true) { if (extension_loaded('intl') && $transliteration == true) { From a006a24075b224bc74c31d7db313c54a4f9db049 Mon Sep 17 00:00:00 2001 From: David Connelly Date: Wed, 5 Oct 2022 00:07:31 +0100 Subject: [PATCH 032/100] Correction to validation errors. --- engine/tg_helpers/validation_helper.php | 42 ++----------------------- 1 file changed, 3 insertions(+), 39 deletions(-) diff --git a/engine/tg_helpers/validation_helper.php b/engine/tg_helpers/validation_helper.php index a8185405..6f5956a3 100755 --- a/engine/tg_helpers/validation_helper.php +++ b/engine/tg_helpers/validation_helper.php @@ -573,51 +573,15 @@ public function url_segment($num) { } -function validation_errorsNEW($opening_html=NULL, $closing_html=NULL) { - if (isset($_SESSION['form_submission_errors'])) { - $form_submission_errors = $_SESSION['form_submission_errors']; - $closing_html = (isset($closing_html)) ? $closing_html : false; - - if ((isset($opening_html)) && (gettype($closing_html == 'boolean'))) { - //build individual form field validation error(s) - - if (isset($form_submission_errors[$opening_html])) { - echo '
    '; - echo 'you got this'; - - } - - json($form_submission_errors); - - $validation_err_str = $opening_html; - } else { - //normal error reporting - if (!isset($opening_html)) { - $opening_html = '

    '; - $closing_html = '

    '; - } - - foreach($form_submission_errors as $form_submission_error) { - $validation_err_str.= $opening_html.$form_submission_error.$closing_html; - } - - unset($_SESSION['form_submission_errors']); - } - - return $validation_err_str; - } - -} - - function validation_errors($opening_html=NULL, $closing_html=NULL) { + if (isset($_SESSION['form_submission_errors'])) { $validation_err_str = ''; $validation_errors = []; $closing_html = (isset($closing_html)) ? $closing_html : false; $form_submission_errors = $_SESSION['form_submission_errors']; - if ((isset($opening_html)) && (gettype($closing_html == 'boolean'))) { + if ((isset($opening_html)) && (gettype($closing_html) == 'boolean')) { //build individual form field validation error(s) if (isset($form_submission_errors[$opening_html])) { $validation_err_str.= '
    '; @@ -651,4 +615,4 @@ function validation_errors($opening_html=NULL, $closing_html=NULL) { } echo $validation_err_str; } -} +} \ No newline at end of file From b1eeafe4c639937094819219e2900361766785c3 Mon Sep 17 00:00:00 2001 From: Simon Field Date: Wed, 5 Oct 2022 22:23:42 +1100 Subject: [PATCH 033/100] fix get method --- engine/Model.php | 181 +++++++++++++++++++++++++---------------------- 1 file changed, 98 insertions(+), 83 deletions(-) diff --git a/engine/Model.php b/engine/Model.php index 14cef68d..edf74738 100755 --- a/engine/Model.php +++ b/engine/Model.php @@ -29,29 +29,29 @@ public function __construct($current_module = NULL) { PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION ); - try{ + try { $this->dbh = new PDO($dsn, $this->user, $this->pass, $options); - } catch(PDOException $e){ + } catch (PDOException $e) { $this->error = $e->getMessage(); - echo $this->error; die(); + echo $this->error; + die(); } - } private function get_param_type($value) { - switch(true){ + switch (true) { case is_int($value): - $type = PDO::PARAM_INT; - break; - case is_bool($value): - $type = PDO::PARAM_BOOL; - break; - case is_null($value): - $type = PDO::PARAM_NULL; - break; - default: - $type = PDO::PARAM_STR; + $type = PDO::PARAM_INT; + break; + case is_bool($value): + $type = PDO::PARAM_BOOL; + break; + case is_null($value): + $type = PDO::PARAM_NULL; + break; + default: + $type = PDO::PARAM_STR; } return $type; @@ -71,9 +71,7 @@ function prepare_and_execute($sql, $data) { } return $this->stmt->execute(); - } - } private function get_table_from_url() { @@ -83,8 +81,8 @@ private function get_table_from_url() { private function correct_tablename($target_tbl) { $bits = explode('-', $target_tbl); $num_bits = count($bits); - if ($num_bits>1) { - $target_tbl = $bits[$num_bits-1]; + if ($num_bits > 1) { + $target_tbl = $bits[$num_bits - 1]; } return $target_tbl; @@ -94,14 +92,25 @@ private function add_limit_offset($sql, $limit, $offset) { if ((is_numeric($limit)) && (is_numeric($offset))) { $limit_results = true; - $sql.= " LIMIT $offset, $limit"; + $sql .= " LIMIT $offset, $limit"; } return $sql; + } + protected function _get_all_tables() { + $tables = []; + $sql = 'show tables'; + $column_name = 'Tables_in_' . DATABASE; + $rows = $this->query($sql, 'array'); + foreach ($rows as $row) { + $tables[] = $row[$column_name]; + } + + return $tables; } - public function get($order_by=NULL, $target_tbl=NULL, $limit=NULL, $offset=NULL) { + public function get($order_by = NULL, $target_tbl = NULL, $limit = NULL, $offset = NULL) { $limit_results = false; @@ -113,6 +122,28 @@ public function get($order_by=NULL, $target_tbl=NULL, $limit=NULL, $offset=NULL) $target_tbl = $this->get_table_from_url(); } + $all_tables = $this->_get_all_tables(); + if (!in_array($target_tbl, $all_tables)) { + http_response_code(422); + echo 'invalid table name'; + die(); + } else { + $sql_x = 'DESCRIBE ' . $target_tbl; + $result_x = $this->query($sql_x, 'object'); + $is_valid_order_by = false; + foreach ($result_x as $row_x) { + if ($row_x->Field == $order_by) { + $is_valid_order_by = true; + } + } + + if ($is_valid_order_by == false) { + echo 'invalid order by value'; + die(); + } + } + + $sql = "SELECT * FROM $target_tbl order by $order_by"; if ((isset($limit)) && (isset($offset))) { @@ -141,10 +172,9 @@ public function get($order_by=NULL, $target_tbl=NULL, $limit=NULL, $offset=NULL) $stmt->execute(); $query = $stmt->fetchAll(PDO::FETCH_OBJ); return $query; - } - public function get_where_custom($column, $value, $operator='=', $order_by='id', $target_tbl=NULL, $limit=NULL, $offset=NULL) { + public function get_where_custom($column, $value, $operator = '=', $order_by = 'id', $target_tbl = NULL, $limit = NULL, $offset = NULL) { if (!isset($target_tbl)) { $target_tbl = $this->get_table_from_url(); @@ -166,12 +196,11 @@ public function get_where_custom($column, $value, $operator='=', $order_by='id', $operator = strtoupper($operator); if (($operator == 'LIKE') || ($operator == 'NOT LIKE')) { - $value = '%'.$value.'%'; + $value = '%' . $value . '%'; $data[$column] = $value; } $query_to_execute = $this->show_query($sql, $data, $this->query_caveat); - } $result = $this->prepare_and_execute($sql, $data); @@ -180,11 +209,10 @@ public function get_where_custom($column, $value, $operator='=', $order_by='id', $items = $this->stmt->fetchAll(PDO::FETCH_OBJ); return $items; } - } //fetch a single record - public function get_where($id, $target_tbl=NULL) { + public function get_where($id, $target_tbl = NULL) { $data['id'] = (int) $id; @@ -204,11 +232,10 @@ public function get_where($id, $target_tbl=NULL) { $item = $this->stmt->fetch(PDO::FETCH_OBJ); return $item; } - } //fetch a single record (alternative version) - public function get_one_where($column, $value, $target_tbl=NULL) { + public function get_one_where($column, $value, $target_tbl = NULL) { $data[$column] = $value; if (!isset($target_tbl)) { @@ -229,21 +256,21 @@ public function get_one_where($column, $value, $target_tbl=NULL) { } } - public function get_many_where($column, $value, $target_tbl=NULL) { + public function get_many_where($column, $value, $target_tbl = NULL) { if (!isset($target_tbl)) { $target_tbl = $this->get_table_from_url(); } $data[$column] = $value; - $sql = 'select * from '.$target_tbl.' where '.$column.' = :'.$column; + $sql = 'select * from ' . $target_tbl . ' where ' . $column . ' = :' . $column; $query = $this->query_bind($sql, $data, 'object'); return $query; } - public function count($target_tbl=NULL) { + public function count($target_tbl = NULL) { //return number of rows on a table if (!isset($target_tbl)) { @@ -263,10 +290,9 @@ public function count($target_tbl=NULL) { $obj = $this->stmt->fetch(PDO::FETCH_OBJ); return $obj->total; } - } - public function count_where($column, $value, $operator='=', $order_by='id', $target_tbl=NULL, $limit=NULL, $offset=NULL) { + public function count_where($column, $value, $operator = '=', $order_by = 'id', $target_tbl = NULL, $limit = NULL, $offset = NULL) { //return number of rows on table (with query customisation) $query = $this->get_where_custom($column, $value, $operator, $order_by, $target_tbl, $limit, $offset); @@ -274,7 +300,7 @@ public function count_where($column, $value, $operator='=', $order_by='id', $tar return $num_rows; } - public function count_rows($column, $value, $target_tbl=NULL) { + public function count_rows($column, $value, $target_tbl = NULL) { //simplified version of count_where (accepts one condition) if (!isset($target_tbl)) { @@ -282,7 +308,7 @@ public function count_rows($column, $value, $target_tbl=NULL) { } $data[$column] = $value; - $sql = 'SELECT COUNT(id) as total from '.$target_tbl.' where '.$column.' = :'.$column; + $sql = 'SELECT COUNT(id) as total from ' . $target_tbl . ' where ' . $column . ' = :' . $column; if ($this->debug == true) { $query_to_execute = $this->show_query($sql, $data); @@ -294,10 +320,9 @@ public function count_rows($column, $value, $target_tbl=NULL) { $obj = $this->stmt->fetch(PDO::FETCH_OBJ); return $obj->total; } - } - public function get_max($target_tbl=NULL) { + public function get_max($target_tbl = NULL) { if (!isset($target_tbl)) { $target_tbl = $this->get_table_from_url(); @@ -317,10 +342,9 @@ public function get_max($target_tbl=NULL) { $max_id = $assoc['max_id']; return $max_id; } - } - public function show_query($query, $data, $caveat=NULL) { + public function show_query($query, $data, $caveat = NULL) { $keys = array(); $values = $data; $named_params = true; @@ -329,7 +353,7 @@ public function show_query($query, $data, $caveat=NULL) { foreach ($data as $key => $value) { if (is_string($key)) { - $keys[] = '/:'.$key.'/'; + $keys[] = '/:' . $key . '/'; } else { $keys[] = '/[?]/'; $named_params = false; @@ -349,64 +373,62 @@ public function show_query($query, $data, $caveat=NULL) { $query = preg_replace($keys, $values, $query); } else { - $query = $query.' '; + $query = $query . ' '; $bits = explode(' ? ', $query); $query = ''; - for ($i=0; $i < count($bits); $i++) { - $query.= $bits[$i]; + for ($i = 0; $i < count($bits); $i++) { + $query .= $bits[$i]; if (isset($values[$i])) { - $query.= ' '.$values[$i].' '; + $query .= ' ' . $values[$i] . ' '; } - } - } if (!isset($caveat)) { $caveat_info = ''; } else { - $caveat_info = '

    PLEASE NOTE: '.$caveat; - $caveat_info.= ' PDO currently has no means of displaying previous query executed.
    '; + $caveat_info = '

    PLEASE NOTE: ' . $caveat; + $caveat_info .= ' PDO currently has no means of displaying previous query executed.
    '; } echo '
    QUERY TO BE EXECUTED:

    -> '; - echo $query.$caveat_info.'
    '; - ?> - - + echo $query . $caveat_info . '
    '; +?> + + - get_table_from_url(); } - $sql = 'INSERT INTO `'.$target_tbl.'` ('; - $sql.= '`'.implode("`, `", array_keys($data)).'`)'; - $sql.= ' VALUES ('; + $sql = 'INSERT INTO `' . $target_tbl . '` ('; + $sql .= '`' . implode("`, `", array_keys($data)) . '`)'; + $sql .= ' VALUES ('; foreach ($data as $key => $value) { - $sql.=':'.$key.', '; + $sql .= ':' . $key . ', '; } $sql = rtrim($sql, ', '); - $sql.=')'; + $sql .= ')'; if ($this->debug == true) { $query_to_execute = $this->show_query($sql, $data, $this->query_caveat); @@ -417,7 +439,7 @@ public function insert($data, $target_tbl=NULL) { return $id; } - public function update($update_id, $data, $target_tbl=NULL) { + public function update($update_id, $data, $target_tbl = NULL) { if (!isset($target_tbl)) { $target_tbl = $this->get_table_from_url(); @@ -426,11 +448,11 @@ public function update($update_id, $data, $target_tbl=NULL) { $sql = "UPDATE `$target_tbl` SET "; foreach ($data as $key => $value) { - $sql.= "`$key` = :$key, "; + $sql .= "`$key` = :$key, "; } $sql = rtrim($sql, ', '); - $sql.= " WHERE `$target_tbl`.`id` = :id"; + $sql .= " WHERE `$target_tbl`.`id` = :id"; $data['id'] = (int) $update_id; $data = $data; @@ -440,10 +462,9 @@ public function update($update_id, $data, $target_tbl=NULL) { } $this->prepare_and_execute($sql, $data); - } - public function delete($id, $target_tbl=NULL) { + public function delete($id, $target_tbl = NULL) { if (!isset($target_tbl)) { $target_tbl = $this->get_table_from_url(); @@ -457,10 +478,9 @@ public function delete($id, $target_tbl=NULL) { } $this->prepare_and_execute($sql, $data); - } - public function query($sql, $return_type=false) { + public function query($sql, $return_type = false) { //WARNING: very high risk of SQL injection - use with caution! $data = []; @@ -480,12 +500,10 @@ public function query($sql, $return_type=false) { } return $query; - } - } - public function query_bind($sql, $data, $return_type=false) { + public function query_bind($sql, $data, $return_type = false) { if ($this->debug == true) { $query_to_execute = $this->show_query($sql, $data, $this->query_caveat); @@ -502,9 +520,7 @@ public function query_bind($sql, $data, $return_type=false) { } return $query; - } - } public function insert_batch($table, array $records) { @@ -539,5 +555,4 @@ public function exec($sql) { echo 'Feature disabled, since not on \'dev\' mode.'; } } - } From 70ee658639568f5ee3f540fbae41c2b28b8a8cb2 Mon Sep 17 00:00:00 2001 From: David Connelly Date: Wed, 5 Oct 2022 15:25:18 +0100 Subject: [PATCH 034/100] Fixed image uploader. --- engine/tg_helpers/file_validation_helper.php | 6 +++--- engine/tg_helpers/validation_helper.php | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/engine/tg_helpers/file_validation_helper.php b/engine/tg_helpers/file_validation_helper.php index 87014eb8..97755836 100755 --- a/engine/tg_helpers/file_validation_helper.php +++ b/engine/tg_helpers/file_validation_helper.php @@ -8,7 +8,7 @@ $file_checks_to_run[$file_validation_test] = $rule_content; } -$target_file = get_target_file($key); +$target_file = get_target_file(); $target_file = $_FILES[$target_file]; $temp_file_name = $target_file['tmp_name']; $file_size = $target_file['size']/1000; //kilobytes) @@ -136,7 +136,7 @@ function check_file_size($file_size, $file_check_value) { return $result; } -function get_target_file($key) { - $userfile = array_keys($_FILES)[$key]; +function get_target_file() { + $userfile = array_keys($_FILES)[0]; return $userfile; } \ No newline at end of file diff --git a/engine/tg_helpers/validation_helper.php b/engine/tg_helpers/validation_helper.php index 6f5956a3..62e2c8ba 100755 --- a/engine/tg_helpers/validation_helper.php +++ b/engine/tg_helpers/validation_helper.php @@ -52,7 +52,7 @@ private function run_validation_test($validation_data, $rules=null) { $this->valid_email($validation_data); break; case 'validate_file': - $this->validate_file($validation_data, $rules); + $this->validate_file($validation_data['key'], $validation_data['label'], $rules); break; case 'valid_datepicker_us': $this->valid_datepicker_us($validation_data); From a0af23eb6fe1e97bc96ac4e9a5b4d566fe68a656 Mon Sep 17 00:00:00 2001 From: David Connelly Date: Wed, 5 Oct 2022 15:59:17 +0100 Subject: [PATCH 035/100] Changed license number to 1.3.3037 --- engine/license.txt | 2 +- license.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/engine/license.txt b/engine/license.txt index ea0b3ce9..43f1de98 100755 --- a/engine/license.txt +++ b/engine/license.txt @@ -3,7 +3,7 @@ * * An open source PHP framework for web developers who like to break the rules * - * Version: 1.3.3036 + * Version: 1.3.3037 * * This product is released under the MIT License (MIT) * diff --git a/license.txt b/license.txt index ea0b3ce9..43f1de98 100755 --- a/license.txt +++ b/license.txt @@ -3,7 +3,7 @@ * * An open source PHP framework for web developers who like to break the rules * - * Version: 1.3.3036 + * Version: 1.3.3037 * * This product is released under the MIT License (MIT) * From 583be746f5c7e34a1515694efc01d385a6a787b0 Mon Sep 17 00:00:00 2001 From: David Connelly Date: Thu, 6 Oct 2022 16:53:19 +0100 Subject: [PATCH 036/100] added function onto validation helper for filtering out potentially malicious characters --- engine/tg_helpers/form_helper.php | 38 +++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/engine/tg_helpers/form_helper.php b/engine/tg_helpers/form_helper.php index 78de678c..82d8f5cf 100755 --- a/engine/tg_helpers/form_helper.php +++ b/engine/tg_helpers/form_helper.php @@ -261,9 +261,47 @@ function post($field_name, $clean_up=NULL) { if (isset($clean_up)) { $value = trim(strip_tags($value)); + $value = preg_replace('/\s+/', ' ', $value); + + if (!defined('ALLOW_SPECIAL_CHARACTERS')) { + //filter out potentially malicious characters + $value = remove_special_characters($value); + } } } + return $value; +} + +function remove_special_characters($value) { + $value = preg_replace('/[^(\x20-\x7F)]*/','', $value); + $var_type = gettype($value); + switch (strtolower($var_type)) { + case 'string': + $filter = FILTER_SANITIZE_STRING; + break; + case 'int': + $filter = FILTER_SANITIZE_NUMBER_INT; + break; + case 'decimal': + $filter = FILTER_SANITIZE_NUMBER_FLOAT; + break; + case 'float': + $filter = FILTER_SANITIZE_NUMBER_FLOAT; + break; + case 'encoded': + $filter = FILTER_SANITIZE_ENCODED; + break; + case 'url': + $filter = FILTER_SANITIZE_URL; + break; + case 'email': + $filter = FILTER_SANITIZE_EMAIL; + break; + default: + $filter = FILTER_SANITIZE_STRING; + } + $value = filter_var($value, $filter); return $value; } \ No newline at end of file From a251a96f174ec2ef824461889debc6ad1bea4640 Mon Sep 17 00:00:00 2001 From: David Connelly Date: Fri, 7 Oct 2022 02:11:21 +0100 Subject: [PATCH 037/100] Improved 'unique' method on validation helper (making it much more strict). Also improved form_helper by adding optional CHARSET constant. --- engine/tg_helpers/form_helper.php | 2 ++ engine/tg_helpers/validation_helper.php | 18 ++++++++++++++---- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/engine/tg_helpers/form_helper.php b/engine/tg_helpers/form_helper.php index 82d8f5cf..6708c704 100755 --- a/engine/tg_helpers/form_helper.php +++ b/engine/tg_helpers/form_helper.php @@ -276,6 +276,8 @@ function post($field_name, $clean_up=NULL) { function remove_special_characters($value) { $value = preg_replace('/[^(\x20-\x7F)]*/','', $value); + $charset = (defined('CHARSET')) ? CHARSET : 'UTF-8'; + $value = htmlspecialchars($value, ENT_QUOTES, $charset); $var_type = gettype($value); switch (strtolower($var_type)) { case 'string': diff --git a/engine/tg_helpers/validation_helper.php b/engine/tg_helpers/validation_helper.php index 62e2c8ba..03440b8b 100755 --- a/engine/tg_helpers/validation_helper.php +++ b/engine/tg_helpers/validation_helper.php @@ -386,6 +386,8 @@ private function unique($key, $label, $posted_value, $inner_value=null) { return; } + $forbidden_values[] = $posted_value; + $bits = explode(',', $inner_value); if (count($bits) == 2) { $allowed_id = $bits[0]; @@ -400,15 +402,23 @@ private function unique($key, $label, $posted_value, $inner_value=null) { require_once(__DIR__.'/../Model.php'); $model = new Model(); - $sql = 'select * from '.$table_name; //not passing into query to avoid SQl injection + $sql = 'select * from '.$table_name; $rows = $model->query($sql, 'object'); + $forbidden_values[] = trim(strip_tags($posted_value)); + $forbidden_values[] = preg_replace('/\s+/', ' ', $posted_value); + + if (!defined('ALLOW_SPECIAL_CHARACTERS')) { + //filter out potentially malicious characters + $forbidden_values[] = remove_special_characters($posted_value); + } + foreach($rows as $row) { $row_id = $row->id; $row_target_value = $row->$key; - if (($row->id !== $allowed_id) && ($row->$key == $posted_value)) { - $this->form_submission_errors[$key][] = 'The ' . $label . ' that you submitted is already on our system.'; - break; + if ((in_array($row_target_value, $forbidden_values)) && ($row->id !== $allowed_id)) { + $this->form_submission_errors[] = 'The ' . $label . ' that you submitted is already on our system.'; + break; } } } From eed500848c83413444f40d84af3b6b71b4097c1d Mon Sep 17 00:00:00 2001 From: David Connelly Date: Fri, 7 Oct 2022 02:36:16 +0100 Subject: [PATCH 038/100] Correction on validation_helper. --- engine/tg_helpers/validation_helper.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/tg_helpers/validation_helper.php b/engine/tg_helpers/validation_helper.php index 03440b8b..ab2eaaf8 100755 --- a/engine/tg_helpers/validation_helper.php +++ b/engine/tg_helpers/validation_helper.php @@ -417,7 +417,7 @@ private function unique($key, $label, $posted_value, $inner_value=null) { $row_id = $row->id; $row_target_value = $row->$key; if ((in_array($row_target_value, $forbidden_values)) && ($row->id !== $allowed_id)) { - $this->form_submission_errors[] = 'The ' . $label . ' that you submitted is already on our system.'; + $this->form_submission_errors[$key][] = 'The ' . $label . ' that you submitted is already on our system.'; break; } } From fe8f257d2cc1379480c89b26535fcea22f7a975e Mon Sep 17 00:00:00 2001 From: David Connelly Date: Fri, 7 Oct 2022 18:38:58 +0100 Subject: [PATCH 039/100] Refined GET method on the Model.php file. --- engine/Model.php | 47 +++++------------------------------------------ 1 file changed, 5 insertions(+), 42 deletions(-) diff --git a/engine/Model.php b/engine/Model.php index edf74738..c91a3813 100755 --- a/engine/Model.php +++ b/engine/Model.php @@ -110,65 +110,28 @@ protected function _get_all_tables() { return $tables; } - public function get($order_by = NULL, $target_tbl = NULL, $limit = NULL, $offset = NULL) { + public function get($order_by=NULL, $target_tbl=NULL, $limit=NULL, $offset=NULL) { - $limit_results = false; - - if (!isset($order_by)) { - $order_by = 'id'; - } + $order_by = (!isset($order_by)) ? 'id' : $order_by; if (!isset($target_tbl)) { $target_tbl = $this->get_table_from_url(); } - $all_tables = $this->_get_all_tables(); - if (!in_array($target_tbl, $all_tables)) { - http_response_code(422); - echo 'invalid table name'; - die(); - } else { - $sql_x = 'DESCRIBE ' . $target_tbl; - $result_x = $this->query($sql_x, 'object'); - $is_valid_order_by = false; - foreach ($result_x as $row_x) { - if ($row_x->Field == $order_by) { - $is_valid_order_by = true; - } - } - - if ($is_valid_order_by == false) { - echo 'invalid order by value'; - die(); - } - } - - $sql = "SELECT * FROM $target_tbl order by $order_by"; if ((isset($limit)) && (isset($offset))) { + settype($limit, 'int'); + settype($offset, 'int'); $sql = $this->add_limit_offset($sql, $limit, $offset); } if ($this->debug == true) { - - if ($limit_results == true) { - $data['limit'] = $limit; - $data['offset'] = $offset; - } else { - $data = []; - } - + $data = []; $query_to_execute = $this->show_query($sql, $data, $this->query_caveat); } $stmt = $this->dbh->prepare($sql); - - if ($limit_results == true) { - $stmt->bindValue(":limit", $limit, PDO::PARAM_INT); - $stmt->bindValue(":offset", $offset, PDO::PARAM_INT); - } - $stmt->execute(); $query = $stmt->fetchAll(PDO::FETCH_OBJ); return $query; From cbe28ef20bb82f70d700710f6ab27199ca7fc81d Mon Sep 17 00:00:00 2001 From: David Connelly Date: Mon, 10 Oct 2022 13:49:26 +0100 Subject: [PATCH 040/100] Changed expected argument type from mixed to object on dynamic_properties. This will make Trongate work on PHP 7 as well as PHP 8. --- engine/Dynamic_properties.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/Dynamic_properties.php b/engine/Dynamic_properties.php index 00984e6e..2f69b8d1 100644 --- a/engine/Dynamic_properties.php +++ b/engine/Dynamic_properties.php @@ -2,7 +2,7 @@ trait Dynamic_properties { private $attributes = []; - public function __set(string $key, mixed $value): void { + public function __set(string $key, object $value): void { $this->attributes[$key] = $value; } From b80319b937eb1b036509b5046a776b4db2e5447b Mon Sep 17 00:00:00 2001 From: David Connelly Date: Wed, 12 Oct 2022 17:41:34 +0100 Subject: [PATCH 041/100] Improved build_additional_includes method on Template.php. Previously would load script element if array el ended in .js and stylesheet if ended in .css. Now in instances where array el ends in neither js or css, the array el will be added into the HTML code as is. It's a bit more flexible and should not be a breaking change normal use cases. --- templates/controllers/Templates.php | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/templates/controllers/Templates.php b/templates/controllers/Templates.php index 23bed783..3115d4cd 100755 --- a/templates/controllers/Templates.php +++ b/templates/controllers/Templates.php @@ -42,14 +42,17 @@ function _build_additional_includes($files) { $html = ''; foreach ($files as $file) { + $file_bits = explode('.', $file); + $filename_extension = $file_bits[count($file_bits)-1]; - $last_four = substr($file, -4); - - if ($last_four == '.css') { - $html.= $this->_build_css_include_code($file); - + if (($filename_extension !== 'js') && ($filename_extension !== 'css')) { + $html.= $file; } else { - $html.= $this->_build_js_include_code($file); + if ($filename_extension = 'js') { + $html.= $this->_build_js_include_code($file); + } else { + $html.= $this->_build_css_include_code($file); + } } $html.= ' @@ -59,6 +62,7 @@ function _build_additional_includes($files) { $html = trim($html); $html.= ' '; + return $html; } From 82a7d81ae30486bab5a9b2602cdf6eed4bd43aba Mon Sep 17 00:00:00 2001 From: David Connelly Date: Fri, 14 Oct 2022 10:22:08 +0100 Subject: [PATCH 042/100] correction to build_additional_includes() method inside Template controller --- templates/controllers/Templates.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/controllers/Templates.php b/templates/controllers/Templates.php index 3115d4cd..4297194a 100755 --- a/templates/controllers/Templates.php +++ b/templates/controllers/Templates.php @@ -43,7 +43,7 @@ function _build_additional_includes($files) { $html = ''; foreach ($files as $file) { $file_bits = explode('.', $file); - $filename_extension = $file_bits[count($file_bits)-1]; + $filename_extension == $file_bits[count($file_bits)-1]; if (($filename_extension !== 'js') && ($filename_extension !== 'css')) { $html.= $file; From 8f87c650632a374cca68ba0a1974926260b9d247 Mon Sep 17 00:00:00 2001 From: David Connelly Date: Fri, 14 Oct 2022 10:38:53 +0100 Subject: [PATCH 043/100] correction to TrongateCSS - now making cursor auto upon td hover (previously was set to 'pointer') --- public/css/trongate.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/css/trongate.css b/public/css/trongate.css index 8e74f28a..1279922b 100755 --- a/public/css/trongate.css +++ b/public/css/trongate.css @@ -152,7 +152,7 @@ th, td { } td:hover { - cursor: pointer; + cursor: auto; } tr:hover, tr:nth-child(odd):hover { From 8618e771d139945ccca7de142dc0e164cd8f3237 Mon Sep 17 00:00:00 2001 From: David Connelly Date: Thu, 20 Oct 2022 13:14:27 +0100 Subject: [PATCH 044/100] 1). improved post method true feature. 2). added attempt_truncate() method to model file and 3). added additional container classes to trongateCSS' --- engine/Model.php | 9 +++++++ engine/tg_helpers/form_helper.php | 43 +++---------------------------- public/css/trongate.css | 24 +++++++++++++++++ 3 files changed, 36 insertions(+), 40 deletions(-) diff --git a/engine/Model.php b/engine/Model.php index c91a3813..da8e74cf 100755 --- a/engine/Model.php +++ b/engine/Model.php @@ -486,6 +486,15 @@ public function query_bind($sql, $data, $return_type = false) { } } + public function attempt_truncate($tablename) { + $num_rows = $this->count($tablename); + + if ($num_rows == 0) { + $sql = 'TRUNCATE '.$tablename; + $this->query($sql); + } + } + public function insert_batch($table, array $records) { //WARNING: Never let your website visitors invoke this method! diff --git a/engine/tg_helpers/form_helper.php b/engine/tg_helpers/form_helper.php index 6708c704..6da85557 100755 --- a/engine/tg_helpers/form_helper.php +++ b/engine/tg_helpers/form_helper.php @@ -261,49 +261,12 @@ function post($field_name, $clean_up=NULL) { if (isset($clean_up)) { $value = trim(strip_tags($value)); - $value = preg_replace('/\s+/', ' ', $value); - - if (!defined('ALLOW_SPECIAL_CHARACTERS')) { - //filter out potentially malicious characters - $value = remove_special_characters($value); - } + $value = preg_replace("/[ ]+/", " ", $value); + $charset = (defined('CHARSET')) ? CHARSET : 'UTF-8'; + $value = htmlspecialchars($value, ENT_QUOTES, $charset); } } - return $value; -} - -function remove_special_characters($value) { - $value = preg_replace('/[^(\x20-\x7F)]*/','', $value); - $charset = (defined('CHARSET')) ? CHARSET : 'UTF-8'; - $value = htmlspecialchars($value, ENT_QUOTES, $charset); - $var_type = gettype($value); - switch (strtolower($var_type)) { - case 'string': - $filter = FILTER_SANITIZE_STRING; - break; - case 'int': - $filter = FILTER_SANITIZE_NUMBER_INT; - break; - case 'decimal': - $filter = FILTER_SANITIZE_NUMBER_FLOAT; - break; - case 'float': - $filter = FILTER_SANITIZE_NUMBER_FLOAT; - break; - case 'encoded': - $filter = FILTER_SANITIZE_ENCODED; - break; - case 'url': - $filter = FILTER_SANITIZE_URL; - break; - case 'email': - $filter = FILTER_SANITIZE_EMAIL; - break; - default: - $filter = FILTER_SANITIZE_STRING; - } - $value = filter_var($value, $filter); return $value; } \ No newline at end of file diff --git a/public/css/trongate.css b/public/css/trongate.css index 1279922b..1a40a9d1 100755 --- a/public/css/trongate.css +++ b/public/css/trongate.css @@ -199,6 +199,30 @@ hr { padding: 1em; } +.container-xxs { + max-width: 450px; +} + +.container-xs { + max-width: 640px; +} + +.container-sm { + max-width: 760px; +} + +.container-lg { + max-width: 960px; +} + +.container-xl { + max-width: 1100px; +} + +.container-xxl { + max-width: 1300px; +} + .pagination { display: inline-block; margin: 1em 0; From e2ecff46534f31387fae239abde2582ef6be8f20 Mon Sep 17 00:00:00 2001 From: David Connelly Date: Mon, 24 Oct 2022 13:01:47 +0100 Subject: [PATCH 045/100] now automatically converting posted numbers to type of int or double when post() method is invoked with true passed as a second argument --- engine/tg_helpers/form_helper.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/engine/tg_helpers/form_helper.php b/engine/tg_helpers/form_helper.php index 6da85557..e04ec053 100755 --- a/engine/tg_helpers/form_helper.php +++ b/engine/tg_helpers/form_helper.php @@ -264,6 +264,11 @@ function post($field_name, $clean_up=NULL) { $value = preg_replace("/[ ]+/", " ", $value); $charset = (defined('CHARSET')) ? CHARSET : 'UTF-8'; $value = htmlspecialchars($value, ENT_QUOTES, $charset); + + if (is_numeric($value)) { + $var_type = (is_numeric(strpos($value, '.'))) ? 'double' : 'int'; + settype($value, $var_type); + } } } From ada2edb2c7661419f49954d9708803faec796859 Mon Sep 17 00:00:00 2001 From: David Connelly Date: Sat, 12 Nov 2022 18:26:51 +0000 Subject: [PATCH 046/100] correction to Templates controller (there was a typo that was creating a syntax error) --- templates/controllers/Templates.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/controllers/Templates.php b/templates/controllers/Templates.php index 4297194a..3115d4cd 100755 --- a/templates/controllers/Templates.php +++ b/templates/controllers/Templates.php @@ -43,7 +43,7 @@ function _build_additional_includes($files) { $html = ''; foreach ($files as $file) { $file_bits = explode('.', $file); - $filename_extension == $file_bits[count($file_bits)-1]; + $filename_extension = $file_bits[count($file_bits)-1]; if (($filename_extension !== 'js') && ($filename_extension !== 'css')) { $html.= $file; From 0a1d665a1d57c60eb465c92b90026b945d61d9b7 Mon Sep 17 00:00:00 2001 From: David Connelly Date: Sat, 12 Nov 2022 18:35:23 +0000 Subject: [PATCH 047/100] typo correction --- templates/controllers/Templates.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/controllers/Templates.php b/templates/controllers/Templates.php index 3115d4cd..3698c69e 100755 --- a/templates/controllers/Templates.php +++ b/templates/controllers/Templates.php @@ -48,7 +48,7 @@ function _build_additional_includes($files) { if (($filename_extension !== 'js') && ($filename_extension !== 'css')) { $html.= $file; } else { - if ($filename_extension = 'js') { + if ($filename_extension == 'js') { $html.= $this->_build_js_include_code($file); } else { $html.= $this->_build_css_include_code($file); From a2156cee18c1ecbf8842d735e47ef85c9ba04ade Mon Sep 17 00:00:00 2001 From: David Connelly Date: Sun, 13 Nov 2022 13:35:25 +0000 Subject: [PATCH 048/100] improved app.js and admin.js so that side nav only gets activated when slide nav exists on the page --- public/js/admin.js | 4 +++- public/js/app.js | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/public/js/admin.js b/public/js/admin.js index 7b1d9094..9cb39557 100755 --- a/public/js/admin.js +++ b/public/js/admin.js @@ -434,7 +434,9 @@ var slideNavLinks = document.querySelector("#slide-nav ul"); var autoPopulateSlideNav = slideNavLinks.getAttribute("auto-populate"); if (autoPopulateSlideNav == "true") { var leftNavLinks = document.querySelector("#left-nav ul"); - slideNavLinks.innerHTML = leftNavLinks.innerHTML; + if (leftNavLinks !== null) { + slideNavLinks.innerHTML = leftNavLinks.innerHTML; + } } body.addEventListener('click', (ev) => { diff --git a/public/js/app.js b/public/js/app.js index 94cb0b7f..4a2e9151 100755 --- a/public/js/app.js +++ b/public/js/app.js @@ -101,7 +101,9 @@ if (slideNavLinks !== null) { var autoPopulateSlideNav = slideNavLinks.getAttribute("auto-populate"); if (autoPopulateSlideNav == "true") { var navLinks = document.querySelector("#top-nav"); - slideNavLinks.innerHTML = navLinks.innerHTML; + if (navLinks !== null) { + slideNavLinks.innerHTML = navLinks.innerHTML; + } } body.addEventListener('click', (ev) => { From c634e52c0e903f4f2498b152cb85e1f5672b53cf Mon Sep 17 00:00:00 2001 From: David Connelly Date: Sun, 13 Nov 2022 15:36:06 +0000 Subject: [PATCH 049/100] moved ip_address() function from Trongate.php into url_helper.php so that it can be invoked without typing $this-> --- engine/Trongate.php | 4 ---- engine/tg_helpers/url.php | 4 ++++ 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/engine/Trongate.php b/engine/Trongate.php index ed41afdb..eaed07f3 100755 --- a/engine/Trongate.php +++ b/engine/Trongate.php @@ -321,8 +321,4 @@ public function upload_file($config) { } } - public function ip_address() { - return $_SERVER['REMOTE_ADDR']; - } - } \ No newline at end of file diff --git a/engine/tg_helpers/url.php b/engine/tg_helpers/url.php index 7ebd2c7f..80a4b376 100755 --- a/engine/tg_helpers/url.php +++ b/engine/tg_helpers/url.php @@ -199,3 +199,7 @@ function json($data, $kill_script = null) { die(); } } + +function ip_address() { + return $_SERVER['REMOTE_ADDR']; +} \ No newline at end of file From 74f986e18b45a427d65d67b0c33e83797a729401 Mon Sep 17 00:00:00 2001 From: David Connelly Date: Sun, 13 Nov 2022 15:59:59 +0000 Subject: [PATCH 050/100] now auto-trimming $_POST vars before form validation happens; --- engine/tg_helpers/validation_helper.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/engine/tg_helpers/validation_helper.php b/engine/tg_helpers/validation_helper.php index ab2eaaf8..d0856a31 100755 --- a/engine/tg_helpers/validation_helper.php +++ b/engine/tg_helpers/validation_helper.php @@ -15,6 +15,11 @@ public function set_rules($key, $label, $rules) { $posted_value = $_FILES[$key]; $tests_to_run[] = 'validate_file'; } else { + + if (isset($_POST[$key])) { + $_POST[$key] = trim($_POST[$key]); + } + $posted_value = isset($_POST[$key]) ? $_POST[$key] : ''; $tests_to_run = $this->get_tests_to_run($rules); } From 8d287673c901e02aa1e876daca39e834c9e72e75 Mon Sep 17 00:00:00 2001 From: David Connelly Date: Mon, 28 Nov 2022 15:39:47 +0000 Subject: [PATCH 051/100] Have now made ignition.php (not bootstrap.php) the start point for the loading of the framework (a vote was taken and it was agreed that 'ignition' was a more meaningful name). Have also simplified the main Trongate.php class so that the heavy lifting for file uploading is handled by file_helper and img_helper classes. --- engine/Core.php | 2 - engine/Dynamic_properties.php | 5 + engine/Trongate.php | 153 +----------------- engine/bootstrap.php | 53 +----- engine/{get_segments.php => ignition.php} | 50 +++++- engine/license.txt | 2 +- engine/tg_helpers/file_helper.php | 46 ++++++ engine/tg_helpers/file_validation_helper.php | 0 engine/tg_helpers/flashdata_helper.php | 0 engine/tg_helpers/form_helper.php | 0 engine/tg_helpers/img_helper.php | 107 ++++++++++++ engine/tg_helpers/{url.php => url_helper.php} | 0 engine/tg_helpers/validation_helper.php | 4 + public/index.php | 2 +- 14 files changed, 224 insertions(+), 200 deletions(-) rename engine/{get_segments.php => ignition.php} (68%) mode change 100755 => 100644 create mode 100644 engine/tg_helpers/file_helper.php mode change 100755 => 100644 engine/tg_helpers/file_validation_helper.php mode change 100755 => 100644 engine/tg_helpers/flashdata_helper.php mode change 100755 => 100644 engine/tg_helpers/form_helper.php create mode 100644 engine/tg_helpers/img_helper.php rename engine/tg_helpers/{url.php => url_helper.php} (100%) mode change 100755 => 100644 mode change 100755 => 100644 engine/tg_helpers/validation_helper.php diff --git a/engine/Core.php b/engine/Core.php index c16c584f..616d2290 100644 --- a/engine/Core.php +++ b/engine/Core.php @@ -7,7 +7,6 @@ class Core { protected $current_value = ''; public function __construct() { - if (strpos(ASSUMED_URL, '/vendor/')) { $this->serve_vendor_asset(); } elseif(strpos(ASSUMED_URL, MODULE_ASSETS_TRIGGER) === false) { @@ -15,7 +14,6 @@ public function __construct() { } else { $this->serve_module_asset(); } - } private function serve_vendor_asset() { diff --git a/engine/Dynamic_properties.php b/engine/Dynamic_properties.php index 2f69b8d1..4165c0d8 100644 --- a/engine/Dynamic_properties.php +++ b/engine/Dynamic_properties.php @@ -7,6 +7,11 @@ public function __set(string $key, object $value): void { } public function __get(string $key) { + + if (!isset($this->attributes[$key])) { + $class_name = ucfirst($key); + $this->$key = new $key; + } return $this->attributes[$key]; } } \ No newline at end of file diff --git a/engine/Trongate.php b/engine/Trongate.php index eaed07f3..8687706a 100755 --- a/engine/Trongate.php +++ b/engine/Trongate.php @@ -5,28 +5,18 @@ class Trongate { protected $modules; protected $model; - protected $validation_helper; protected $url; protected $module_name; protected $parent_module = ''; protected $child_module = ''; public function __construct($module_name=NULL) { - $this->module_name = $module_name; $this->modules = new Modules; - //load the helper classes - foreach (TRONGATE_HELPERS as $tg_helper) { - require_once 'tg_helpers/'.$tg_helper.'.php'; - } - - $this->validation_helper = new Validation_helper; - //load the model class require_once 'Model.php'; $this->model = new Model($module_name); - } public function load($helper) { @@ -177,148 +167,11 @@ private function load_child_view($view, $data) { } public function upload_picture($data) { - - if (!isset($data['upload_to_module'])) { - $data['upload_to_module'] = false; - } - - //check for valid image width and mime type - $userfile = array_keys($_FILES)[0]; - $target_file = $_FILES[$userfile]; - - $dimension_data = getimagesize($target_file['tmp_name']); - $image_width = $dimension_data[0]; - - if (!is_numeric($image_width)) { - die('ERROR: non numeric image width'); - } - - $content_type = mime_content_type($target_file['tmp_name']); - - $str = substr($content_type, 0, 6); - if ($str !== 'image/') { - die('ERROR: not an image.'); - } - - $tmp_name = $target_file['tmp_name']; - $data['image'] = new Image($tmp_name); - $data['tmp_file_width'] = $data['image']->getWidth(); - $data['tmp_file_height'] = $data['image']->getHeight(); - - if ($data['upload_to_module'] == true) { - $target_module = (isset($data['targetModule']) ? $data['targetModule'] : segment(1)); - $data['filename'] = '../modules/'.$target_module.'/assets/'.$data['destination'].'/'.$target_file['name']; - } else { - $data['filename'] = '../public/'.$data['destination'].'/'.$target_file['name']; - } - - if (!isset($data['max_width'])) { - $data['max_width'] = NULL; - } - - if (!isset($data['max_height'])) { - $data['max_height'] = NULL; - } - - $this->save_that_pic($data); - - //rock the thumbnail - if ((isset($data['thumbnail_max_width'])) && (isset($data['thumbnail_max_height'])) && (isset($data['thumbnail_dir']))) { - $ditch = $data['destination']; - $replace = $data['thumbnail_dir']; - $data['filename'] = str_replace($ditch, $replace, $data['filename']); - $data['max_width'] = $data['thumbnail_max_width']; - $data['max_height'] = $data['thumbnail_max_height']; - $this->save_that_pic($data); - } - } - - private function save_that_pic($data) { - extract($data); - $reduce_width = false; - $reduce_height = false; - - if (!isset($data['compression'])) { - $compression = 100; - } else { - $compression = $data['compression']; - } - - if (!isset($data['permissions'])) { - $permissions = 775; - } else { - $permissions = $data['permissions']; - } - - //do we need to resize the picture? - if ((isset($max_width)) && ($tmp_file_width>$max_width)) { - $reduce_width = true; - $resize_factor_w = $tmp_file_width / $max_width; - } - - if ((isset($max_height)) && ($tmp_file_width>$max_height)) { - $reduce_height = true; - $resize_factor_h = $tmp_file_height / $max_height; - } - - if ((isset($resize_factor_w)) && (isset($resize_factor_h))) { - if ($resize_factor_w > $resize_factor_h) { - $reduce_height = false; - } else { - $reduce_width = false; - } - } - - //either do the height resize or the width resize - never both - if ($reduce_width == true) { - $image->resizeToWidth($max_width); - } elseif($reduce_height == true) { - $image->resizeToHeight($max_height); - } - - $image->save($filename, $compression); + $this->img_helper->upload($data); } - public function upload_file($config) { - extract($config); - - if (!isset($destination)) { - die('ERROR: upload requires inclusion of \'destination\' property. Check documentation for details.'); - } - - $userfile = array_keys($_FILES)[0]; - $target_file = $_FILES[$userfile]; - - if (!isset($new_file_name)) { - $new_file_name = $target_file['name']; - } elseif ($new_file_name === true) { - $characters = '23456789abcdefghjkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ'; - $randomString = ''; - for ($i = 0; $i < 10; $i++) { - $randomString .= $characters[rand(0, strlen($characters) - 1)]; - } - - $new_file_name = $randomString; - } - - $bits = explode('.', $target_file['name']); - $file_extension = '.'.$bits[count($bits)-1]; - - $new_file_name = str_replace($file_extension, '', $new_file_name); - $new_file_name = ltrim(trim(filter_var($new_file_name, FILTER_SANITIZE_STRING))); - $new_file_name.= $file_extension; - - //make sure the destination folder exists - $target_destination = '../public/'.$destination; - - if (is_dir($target_destination)) { - //upload the temp file to the destination - $new_file_path = $target_destination.'/'.$new_file_name; - move_uploaded_file($target_file['tmp_name'], $new_file_path); - - } else { - die('ERROR: Unable to find target file destination: $destination'); - } + public function upload_file($data) { + $this->img_helper->upload($data); } } \ No newline at end of file diff --git a/engine/bootstrap.php b/engine/bootstrap.php index 155c6abb..e7021f06 100755 --- a/engine/bootstrap.php +++ b/engine/bootstrap.php @@ -1,46 +1,9 @@ ERROR: View file does not exist at: '.$file_path); - } -} - -define('APPPATH', str_replace("\\", "/", dirname(dirname(__FILE__)).'/')); -define('REQUEST_TYPE', $_SERVER['REQUEST_METHOD']); -$tg_helpers = ['form_helper', 'flashdata_helper', 'url', 'validation_helper']; -define('TRONGATE_HELPERS', $tg_helpers); \ No newline at end of file +/* + This file (bootstrap.php) has been left in to avoid backwards compatibility errors. + However, it will be removed from the framework in the future & ignition.php will be + the start point for the loading of the framework. You are therefore encouraged to + delete THIS file (bootstrap.php) and then modify public/index.php so that it + calls ignition, like so ---> require_once '../engine/ignition.php'; +*/ +require_once('ignition.php'); \ No newline at end of file diff --git a/engine/get_segments.php b/engine/ignition.php old mode 100755 new mode 100644 similarity index 68% rename from engine/get_segments.php rename to engine/ignition.php index e21b98c1..5f4348a2 --- a/engine/get_segments.php +++ b/engine/ignition.php @@ -1,4 +1,44 @@ ERROR: View file does not exist at: '.$file_path); + } +} + function get_segments($ignore_custom_routes=NULL) { //figure out how many segments need to be ditched @@ -87,7 +127,15 @@ function attempt_return_nice_url($target_url) { return $target_url; } +define('APPPATH', str_replace("\\", "/", dirname(dirname(__FILE__)).'/')); +define('REQUEST_TYPE', $_SERVER['REQUEST_METHOD']); +$tg_helpers = ['form_helper', 'flashdata_helper', 'img_helper', 'url_helper', 'validation_helper']; +define('TRONGATE_HELPERS', $tg_helpers); $data = get_segments(); - define('SEGMENTS', $data['segments']); define('ASSUMED_URL', $data['assumed_url']); + +//load the helper classes +foreach (TRONGATE_HELPERS as $tg_helper) { + require_once 'tg_helpers/'.$tg_helper.'.php'; +} \ No newline at end of file diff --git a/engine/license.txt b/engine/license.txt index 43f1de98..1d4731f9 100755 --- a/engine/license.txt +++ b/engine/license.txt @@ -3,7 +3,7 @@ * * An open source PHP framework for web developers who like to break the rules * - * Version: 1.3.3037 + * Version: 1.3.3038 * * This product is released under the MIT License (MIT) * diff --git a/engine/tg_helpers/file_helper.php b/engine/tg_helpers/file_helper.php new file mode 100644 index 00000000..6f82d9cf --- /dev/null +++ b/engine/tg_helpers/file_helper.php @@ -0,0 +1,46 @@ +getWidth(); + $data['tmp_file_height'] = $data['image']->getHeight(); + + if ($data['upload_to_module'] == true) { + $target_module = (isset($data['targetModule']) ? $data['targetModule'] : segment(1)); + $data['filename'] = '../modules/'.$target_module.'/assets/'.$data['destination'].'/'.$target_file['name']; + } else { + $data['filename'] = '../public/'.$data['destination'].'/'.$target_file['name']; + } + + if (!isset($data['max_width'])) { + $data['max_width'] = NULL; + } + + if (!isset($data['max_height'])) { + $data['max_height'] = NULL; + } + + $this->save_that_pic($data); + + //rock the thumbnail + if ((isset($data['thumbnail_max_width'])) && (isset($data['thumbnail_max_height'])) && (isset($data['thumbnail_dir']))) { + $ditch = $data['destination']; + $replace = $data['thumbnail_dir']; + $data['filename'] = str_replace($ditch, $replace, $data['filename']); + $data['max_width'] = $data['thumbnail_max_width']; + $data['max_height'] = $data['thumbnail_max_height']; + $this->save_that_pic($data); + } + } + + private function save_that_pic($data) { + extract($data); + $reduce_width = false; + $reduce_height = false; + + if (!isset($data['compression'])) { + $compression = 100; + } else { + $compression = $data['compression']; + } + + if (!isset($data['permissions'])) { + $permissions = 775; + } else { + $permissions = $data['permissions']; + } + + //do we need to resize the picture? + if ((isset($max_width)) && ($tmp_file_width>$max_width)) { + $reduce_width = true; + $resize_factor_w = $tmp_file_width / $max_width; + } + + if ((isset($max_height)) && ($tmp_file_width>$max_height)) { + $reduce_height = true; + $resize_factor_h = $tmp_file_height / $max_height; + } + + if ((isset($resize_factor_w)) && (isset($resize_factor_h))) { + if ($resize_factor_w > $resize_factor_h) { + $reduce_height = false; + } else { + $reduce_width = false; + } + } + + //either do the height resize or the width resize - never both + if ($reduce_width == true) { + $image->resizeToWidth($max_width); + } elseif($reduce_height == true) { + $image->resizeToHeight($max_height); + } + + $image->save($filename, $compression); + } + +} \ No newline at end of file diff --git a/engine/tg_helpers/url.php b/engine/tg_helpers/url_helper.php old mode 100755 new mode 100644 similarity index 100% rename from engine/tg_helpers/url.php rename to engine/tg_helpers/url_helper.php diff --git a/engine/tg_helpers/validation_helper.php b/engine/tg_helpers/validation_helper.php old mode 100755 new mode 100644 index d0856a31..76cae5fb --- a/engine/tg_helpers/validation_helper.php +++ b/engine/tg_helpers/validation_helper.php @@ -4,6 +4,10 @@ class Validation_helper { public $form_submission_errors = []; public $posted_fields = []; + public function say_hello() { + echo 'hello from validation helper'; die(); + } + public function set_rules($key, $label, $rules) { if ((!isset($_POST[$key])) && (isset($_FILES[$key]))) { diff --git a/public/index.php b/public/index.php index ba10ece9..e8e69ba5 100755 --- a/public/index.php +++ b/public/index.php @@ -1,5 +1,5 @@ Date: Mon, 28 Nov 2022 15:52:12 +0000 Subject: [PATCH 052/100] Changed version number to 1.3.3038 --- license.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/license.txt b/license.txt index 43f1de98..1d4731f9 100755 --- a/license.txt +++ b/license.txt @@ -3,7 +3,7 @@ * * An open source PHP framework for web developers who like to break the rules * - * Version: 1.3.3037 + * Version: 1.3.3038 * * This product is released under the MIT License (MIT) * From 6b738647ddaa3305ed26c2ed9742da3288c42aea Mon Sep 17 00:00:00 2001 From: David Connelly Date: Sat, 17 Dec 2022 14:59:57 +0000 Subject: [PATCH 053/100] added class onto Trongate CSS for making sure image widths do not exceed width of parent container --- public/css/trongate.css | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/public/css/trongate.css b/public/css/trongate.css index 1a40a9d1..57f99bfe 100755 --- a/public/css/trongate.css +++ b/public/css/trongate.css @@ -42,6 +42,11 @@ a:hover { color: var(--primary-dark); } +img { + max-width: 100%; + height: auto; +} + input[type="text"], input[type="number"], input[type="email"], input[type="password"], select, textarea { padding: 0.6em 0.6em; font-size: 1em; From 016ea559f7f03e597892c08095874774c3f7f3c1 Mon Sep 17 00:00:00 2001 From: Piraatje Date: Mon, 19 Dec 2022 14:00:59 +0100 Subject: [PATCH 054/100] Replaced insecure string generator with a secure one --- engine/tg_helpers/file_helper.php | 8 +------- engine/tg_helpers/url_helper.php | 15 +++++++++------ .../controllers/Trongate_tokens.php | 14 ++------------ 3 files changed, 12 insertions(+), 25 deletions(-) diff --git a/engine/tg_helpers/file_helper.php b/engine/tg_helpers/file_helper.php index 6f82d9cf..f1cda700 100644 --- a/engine/tg_helpers/file_helper.php +++ b/engine/tg_helpers/file_helper.php @@ -14,13 +14,7 @@ public function upload($config) { if (!isset($new_file_name)) { $new_file_name = $target_file['name']; } elseif ($new_file_name === true) { - $characters = '23456789abcdefghjkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ'; - $randomString = ''; - for ($i = 0; $i < 10; $i++) { - $randomString .= $characters[rand(0, strlen($characters) - 1)]; - } - - $new_file_name = $randomString; + $new_file_name = make_rand_str(10); } $bits = explode('.', $target_file['name']); diff --git a/engine/tg_helpers/url_helper.php b/engine/tg_helpers/url_helper.php index 80a4b376..190d4622 100644 --- a/engine/tg_helpers/url_helper.php +++ b/engine/tg_helpers/url_helper.php @@ -179,14 +179,17 @@ function api_auth() { } } -function make_rand_str($strlen, $uppercase = false) { - $characters = '23456789abcdefghjkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ'; - $random_string = ''; - for ($i = 0; $i < $strlen; $i++) { - $random_string .= $characters[mt_rand(0, strlen($characters) - 1)]; +function make_rand_str($length, $uppercase = false) { + $random_string_length = ($length / 2); + + try { + $random_bytes = random_bytes($random_string_length); + } catch (\Exception) { + exit("Appropriate source of randomness cannot be found"); } + $random_string = bin2hex($random_bytes); - if ($uppercase == true) { + if ($uppercase) { $random_string = strtoupper($random_string); } return $random_string; diff --git a/modules/trongate_tokens/controllers/Trongate_tokens.php b/modules/trongate_tokens/controllers/Trongate_tokens.php index 5097fa31..6420b4d9 100755 --- a/modules/trongate_tokens/controllers/Trongate_tokens.php +++ b/modules/trongate_tokens/controllers/Trongate_tokens.php @@ -247,7 +247,7 @@ function _generate_token($data) { */ //generate 32 digit random string - $random_string = $this->_generate_rand_str(); + $random_string = make_rand_str(); //build data array variables (required for table insert) if (!isset($data['expiry_date'])) { @@ -272,16 +272,6 @@ function _generate_token($data) { return $random_string; } - function _generate_rand_str() { - $token_length = 32; - $characters = '-_0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; - $random_string = ''; - for ($i = 0; $i < $token_length; $i++) { - $random_string .= $characters[mt_rand(0, strlen($characters) - 1)]; - } - return $random_string; - } - function regenerate() { $old_token = segment(3); $expiry_date = segment(4); @@ -300,7 +290,7 @@ function regenerate() { if ($num_rows>0) { $this_token = $tokens[0]; $update_id = $this_token->id; - $new_token = $this->_generate_rand_str(); + $new_token = make_rand_str(); $new_data['user_id'] = $this_token->user_id; $new_data['code'] = $this_token->code; From 0b2522f6aedf384ca5e70787f7f6c18c11a73e62 Mon Sep 17 00:00:00 2001 From: Piraatje Date: Mon, 19 Dec 2022 21:46:39 +0100 Subject: [PATCH 055/100] Make length parameter have a default value --- engine/tg_helpers/url_helper.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/tg_helpers/url_helper.php b/engine/tg_helpers/url_helper.php index 190d4622..f3611786 100644 --- a/engine/tg_helpers/url_helper.php +++ b/engine/tg_helpers/url_helper.php @@ -179,7 +179,7 @@ function api_auth() { } } -function make_rand_str($length, $uppercase = false) { +function make_rand_str($length = 32, $uppercase = false) { $random_string_length = ($length / 2); try { From b47b2f348bdbfd86d298d171ea3942ae1d359494 Mon Sep 17 00:00:00 2001 From: Piraatje Date: Fri, 23 Dec 2022 15:11:49 +0100 Subject: [PATCH 056/100] Make expression more clear --- engine/tg_helpers/url_helper.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/tg_helpers/url_helper.php b/engine/tg_helpers/url_helper.php index f3611786..6af0d8c4 100644 --- a/engine/tg_helpers/url_helper.php +++ b/engine/tg_helpers/url_helper.php @@ -189,7 +189,7 @@ function make_rand_str($length = 32, $uppercase = false) { } $random_string = bin2hex($random_bytes); - if ($uppercase) { + if ($uppercase === true) { $random_string = strtoupper($random_string); } return $random_string; From fac17f4ed099c0d8770add06c8e69dda57bbc990 Mon Sep 17 00:00:00 2001 From: David Connelly Date: Sat, 24 Dec 2022 21:31:36 +0000 Subject: [PATCH 057/100] Added new and improved filter_string() function to form_helper. This function (filter_string()) will be called whenever you pass a second argument of true into the post function --- engine/tg_helpers/form_helper.php | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/engine/tg_helpers/form_helper.php b/engine/tg_helpers/form_helper.php index e04ec053..5a417702 100644 --- a/engine/tg_helpers/form_helper.php +++ b/engine/tg_helpers/form_helper.php @@ -260,10 +260,7 @@ function post($field_name, $clean_up=NULL) { $value = $_POST[$field_name]; if (isset($clean_up)) { - $value = trim(strip_tags($value)); - $value = preg_replace("/[ ]+/", " ", $value); - $charset = (defined('CHARSET')) ? CHARSET : 'UTF-8'; - $value = htmlspecialchars($value, ENT_QUOTES, $charset); + $value = filter_string($value); if (is_numeric($value)) { $var_type = (is_numeric(strpos($value, '.'))) ? 'double' : 'int'; @@ -274,4 +271,17 @@ function post($field_name, $clean_up=NULL) { } return $value; +} + +function filter_string($string) { + // Apply XSS filtering + $string = strip_tags(htmlspecialchars($string)); + + // Convert double spaces to single spaces + $string = preg_replace('/\s+/', ' ', $string); + + // Trim leading and trailing white space + $string = trim($string); + + return $string; } \ No newline at end of file From c63df5e99d708b0563674bb69aee746e84c88de1 Mon Sep 17 00:00:00 2001 From: David Connelly Date: Mon, 26 Dec 2022 18:39:43 +0000 Subject: [PATCH 058/100] improved filter_string() function and added new filter_name() function. Docs will be updated shortly. --- engine/tg_helpers/form_helper.php | 73 ++++++++++++++++++++++++++++++- 1 file changed, 71 insertions(+), 2 deletions(-) diff --git a/engine/tg_helpers/form_helper.php b/engine/tg_helpers/form_helper.php index 5a417702..a538596e 100644 --- a/engine/tg_helpers/form_helper.php +++ b/engine/tg_helpers/form_helper.php @@ -273,9 +273,49 @@ function post($field_name, $clean_up=NULL) { return $value; } -function filter_string($string) { + /* + IMPORTANT NOTE REGARDING STRIP_TAGS(): + + It's possible that you may have to write and use your own, unique + string filter methods depending on your specific use case. With this + being the case, please note that strip_tags function has an optional + second argument, which is a string of allowed HTML tags and attributes. + If you want to allow certain HTML tags or attributes in the string, + you can pass a list of allowed tags and attributes as the second argument. + + Example 1: + + $string = '

    This is a test string.

    '; + $filtered_string = strip_tags($string, ''); + echo $filtered_string; // Outputs: "This is a test string." + + Example 2: + In this example, we allow both 'strong' tags and 'em' tags... + + $string = '

    This is a test string.

    '; + $filtered_string = strip_tags($string, ''); + echo $filtered_string; // Outputs: "This is a test string." + + Example 3: + In this example, we allow the style attribute for the tag... + + $string = '

    This is a test string.

    Emphasis'; + $filtered_string = strip_tags($string, ''); + echo $filtered_string; // Outputs: "This is a test string.Emphasis" + + FINALLY + If you pass an array of allowed tags into strip_tags, before a database insert, + use html_entity_decode() when displaying the stored string in the browser. + */ + +function filter_string($string, $allowed_tags=[]) { + //Potentially suitable for filtering data submitted via textarea. + + //remove HTML & PHP tags (please read note above for more!) + $string = strip_tags($string, $allowed_tags); + // Apply XSS filtering - $string = strip_tags(htmlspecialchars($string)); + $string = htmlspecialchars($string); // Convert double spaces to single spaces $string = preg_replace('/\s+/', ' ', $string); @@ -284,4 +324,33 @@ function filter_string($string) { $string = trim($string); return $string; +} + +function filter_name($name, $allowed_chars=[]) { + //Similar to filter_string() but better suited for usernames etc + + //remove HTML & PHP tags (please read note above for more!) + $name = strip_tags($name); + + // Apply XSS filtering + $name = htmlspecialchars($name); + + // Create a regex pattern that includes the allowed characters + $pattern = '/[^a-zA-Z0-9\s'; + + if (!empty($allowed_chars)) { + $pattern .= '[' . implode('', $allowed_chars) . ']'; + } + $pattern .= '/'; + + // Replace any characters that are not in the allowed list + $name = preg_replace($pattern, '', $name); + + // Convert double spaces to single spaces + $name = preg_replace('/\s+/', ' ', $name); + + // Trim leading and trailing white space + $name = trim($name); + + return $name; } \ No newline at end of file From cae402c9771a99ec2740cb92716aa4bce75cd994 Mon Sep 17 00:00:00 2001 From: David Connelly Date: Mon, 26 Dec 2022 18:53:23 +0000 Subject: [PATCH 059/100] correction on filter_name() --- engine/tg_helpers/form_helper.php | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/engine/tg_helpers/form_helper.php b/engine/tg_helpers/form_helper.php index a538596e..8b6d81c1 100644 --- a/engine/tg_helpers/form_helper.php +++ b/engine/tg_helpers/form_helper.php @@ -337,10 +337,7 @@ function filter_name($name, $allowed_chars=[]) { // Create a regex pattern that includes the allowed characters $pattern = '/[^a-zA-Z0-9\s'; - - if (!empty($allowed_chars)) { - $pattern .= '[' . implode('', $allowed_chars) . ']'; - } + $pattern .= !empty($allowed_chars) ? '[' . implode('', $allowed_chars) . ']' : ']'; $pattern .= '/'; // Replace any characters that are not in the allowed list From 3229610c03948b9fed28fa0ba3eaffa76a80eb3a Mon Sep 17 00:00:00 2001 From: David Connelly Date: Sun, 1 Jan 2023 11:34:58 +0000 Subject: [PATCH 060/100] Fixed make_rand_str() - previously it returned a string with a length of 4, if you passed in an argument of 5. Also improved valid_email form helper. The new one is more strict and, if we have an internet connection, it checks to see if the domain name (in the submitted email address) is valid. --- engine/tg_helpers/url_helper.php | 21 ++++++++--------- engine/tg_helpers/validation_helper.php | 30 +++++++++++++++++++++---- 2 files changed, 35 insertions(+), 16 deletions(-) diff --git a/engine/tg_helpers/url_helper.php b/engine/tg_helpers/url_helper.php index 6af0d8c4..c5ce0198 100644 --- a/engine/tg_helpers/url_helper.php +++ b/engine/tg_helpers/url_helper.php @@ -180,21 +180,18 @@ function api_auth() { } function make_rand_str($length = 32, $uppercase = false) { - $random_string_length = ($length / 2); - - try { - $random_bytes = random_bytes($random_string_length); - } catch (\Exception) { - exit("Appropriate source of randomness cannot be found"); - } - $random_string = bin2hex($random_bytes); - - if ($uppercase === true) { - $random_string = strtoupper($random_string); + $characters = '23456789abcdefghjkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ'; + $charactersLength = strlen($characters); + $randomString = ''; + for ($i = 0; $i < $length; $i++) { + $randomByte = random_bytes(1); + $randomInt = ord($randomByte) % $charactersLength; + $randomString .= $characters[$randomInt]; } - return $random_string; + return $uppercase ? strtoupper($randomString) : $randomString; } + function json($data, $kill_script = null) { echo '
    ' . json_encode($data, JSON_PRETTY_PRINT) . '
    '; diff --git a/engine/tg_helpers/validation_helper.php b/engine/tg_helpers/validation_helper.php index 76cae5fb..b6dedb27 100644 --- a/engine/tg_helpers/validation_helper.php +++ b/engine/tg_helpers/validation_helper.php @@ -4,10 +4,6 @@ class Validation_helper { public $form_submission_errors = []; public $posted_fields = []; - public function say_hello() { - echo 'hello from validation helper'; die(); - } - public function set_rules($key, $label, $rules) { if ((!isset($_POST[$key])) && (isset($_FILES[$key]))) { @@ -451,8 +447,33 @@ private function less_than($key, $label, $posted_value, $inner_value) { private function valid_email($validation_data) { extract($validation_data); + if ((!filter_var($posted_value, FILTER_VALIDATE_EMAIL)) && ($posted_value !== '')) { $this->form_submission_errors[$key][] = 'The '.$label.' field must contain a valid email address.'; + return; + } + + // Check if the email address contains an @ symbol and a valid domain name + $at_pos = strpos($posted_value, '@'); + if ($at_pos === false || $at_pos === 0) { + $this->form_submission_errors[$key][] = 'The '.$label.' is not properly formatted.'; + return; + } + + // Make sure the email address is not too long + if (strlen($posted_value) > 254) { + $this->form_submission_errors[$key][] = 'The '.$label.' is too long.'; + return; + } + + // Check if the internet is available + if($sock = @fsockopen('www.google.com', 80)) { + fclose($sock); + $domain_name = substr($posted_value, $at_pos + 1); + if (!checkdnsrr($domain_name, 'MX')) { + $this->form_submission_errors[$key][] = 'The '.$label.' field contains an invalid domain name'; + return; + } } } @@ -595,6 +616,7 @@ public function url_segment($num) { function validation_errors($opening_html=NULL, $closing_html=NULL) { if (isset($_SESSION['form_submission_errors'])) { + $validation_err_str = ''; $validation_errors = []; $closing_html = (isset($closing_html)) ? $closing_html : false; From b32bb6030651fc16b87576482fdc8abac358718a Mon Sep 17 00:00:00 2001 From: David Connelly Date: Sun, 1 Jan 2023 11:40:33 +0000 Subject: [PATCH 061/100] Changed to v1.3.3039 - this new version number brings an assortment of non-breaking changes that are all to do with improving form validation. --- engine/license.txt | 2 +- license.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/engine/license.txt b/engine/license.txt index 1d4731f9..0bf4900e 100755 --- a/engine/license.txt +++ b/engine/license.txt @@ -3,7 +3,7 @@ * * An open source PHP framework for web developers who like to break the rules * - * Version: 1.3.3038 + * Version: 1.3.3039 * * This product is released under the MIT License (MIT) * diff --git a/license.txt b/license.txt index 1d4731f9..0bf4900e 100755 --- a/license.txt +++ b/license.txt @@ -3,7 +3,7 @@ * * An open source PHP framework for web developers who like to break the rules * - * Version: 1.3.3038 + * Version: 1.3.3039 * * This product is released under the MIT License (MIT) * From 4c85ee1e85ddc3c740cf9f14a40d59550edcd33f Mon Sep 17 00:00:00 2001 From: David Connelly Date: Mon, 2 Jan 2023 14:37:04 +0000 Subject: [PATCH 062/100] tweaked sql_autoload_register inside ignition.php for improved compatibility with Packagist --- engine/ignition.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/engine/ignition.php b/engine/ignition.php index 5f4348a2..14a7412a 100644 --- a/engine/ignition.php +++ b/engine/ignition.php @@ -12,7 +12,13 @@ $class_name = 'tg_helpers/'.$class_name; } - require_once $class_name . '.php'; + $target_filename = realpath(__DIR__.'/'.$class_name.'.php'); + + if (file_exists($target_filename)) { + return require_once($target_filename); + } + + return false; }); function load($template_file, $data=NULL) { From feac308aa17ee1efef6f7d6651c06afaffc529a0 Mon Sep 17 00:00:00 2001 From: David Connelly Date: Tue, 10 Jan 2023 15:37:11 +0000 Subject: [PATCH 063/100] Correction to file uploader. Documentation has been updated. For full details of how file uploading works with Trongate, please refer to https://trongate.io/docs/information/a-general-overview-of-file-uploading --- engine/Trongate.php | 2 +- engine/license.txt | 2 +- engine/tg_helpers/file_helper.php | 21 ++++++++++++--------- license.txt | 2 +- 4 files changed, 15 insertions(+), 12 deletions(-) diff --git a/engine/Trongate.php b/engine/Trongate.php index 8687706a..8d45db9c 100755 --- a/engine/Trongate.php +++ b/engine/Trongate.php @@ -171,7 +171,7 @@ public function upload_picture($data) { } public function upload_file($data) { - $this->img_helper->upload($data); + $this->file_helper->upload($data); } } \ No newline at end of file diff --git a/engine/license.txt b/engine/license.txt index 0bf4900e..ebff4325 100755 --- a/engine/license.txt +++ b/engine/license.txt @@ -3,7 +3,7 @@ * * An open source PHP framework for web developers who like to break the rules * - * Version: 1.3.3039 + * Version: 1.3.3040 * * This product is released under the MIT License (MIT) * diff --git a/engine/tg_helpers/file_helper.php b/engine/tg_helpers/file_helper.php index f1cda700..1b049d5a 100644 --- a/engine/tg_helpers/file_helper.php +++ b/engine/tg_helpers/file_helper.php @@ -11,18 +11,21 @@ public function upload($config) { $userfile = array_keys($_FILES)[0]; $target_file = $_FILES[$userfile]; - if (!isset($new_file_name)) { - $new_file_name = $target_file['name']; - } elseif ($new_file_name === true) { - $new_file_name = make_rand_str(10); + if(!isset($make_rand_name)) { + $make_rand_name = false; } - $bits = explode('.', $target_file['name']); - $file_extension = '.'.$bits[count($bits)-1]; + $new_file_name = ($make_rand_name === true) ? make_rand_str(10) : $target_file['name']; - $new_file_name = str_replace($file_extension, '', $new_file_name); - $new_file_name = ltrim(trim(filter_var($new_file_name, FILTER_SANITIZE_STRING))); - $new_file_name.= $file_extension; + if ($make_rand_name === true) { + //add file extension onto rand file name + $bits = explode('.', $target_file['name']); + $file_extension = '.'.$bits[count($bits)-1]; + + $new_file_name = str_replace($file_extension, '', $new_file_name); + $new_file_name = ltrim(trim(filter_var($new_file_name, FILTER_SANITIZE_STRING))); + $new_file_name.= $file_extension; + } //make sure the destination folder exists $target_destination = '../public/'.$destination; diff --git a/license.txt b/license.txt index 0bf4900e..ebff4325 100755 --- a/license.txt +++ b/license.txt @@ -3,7 +3,7 @@ * * An open source PHP framework for web developers who like to break the rules * - * Version: 1.3.3039 + * Version: 1.3.3040 * * This product is released under the MIT License (MIT) * From de11061ea88fc8a1a23aa3f0820b62a4a6c1e672 Mon Sep 17 00:00:00 2001 From: David Connelly Date: Sat, 14 Jan 2023 16:46:33 +0000 Subject: [PATCH 064/100] vastly improved file and image uploaders - please refer to docs of release notes for more info --- engine/Trongate.php | 6 +- engine/license.txt | 2 +- engine/tg_helpers/file_helper.php | 59 ++++++++--- engine/tg_helpers/img_helper.php | 167 +++++++++++++++++++----------- engine/tg_helpers/url_helper.php | 11 +- license.txt | 2 +- 6 files changed, 169 insertions(+), 78 deletions(-) diff --git a/engine/Trongate.php b/engine/Trongate.php index 8d45db9c..84731b3c 100755 --- a/engine/Trongate.php +++ b/engine/Trongate.php @@ -167,11 +167,13 @@ private function load_child_view($view, $data) { } public function upload_picture($data) { - $this->img_helper->upload($data); + $uploaded_file_info = $this->img_helper->upload($data); + return $uploaded_file_info; } public function upload_file($data) { - $this->file_helper->upload($data); + $uploaded_file_info = $this->file_helper->upload($data); + return $uploaded_file_info; } } \ No newline at end of file diff --git a/engine/license.txt b/engine/license.txt index ebff4325..32dc4035 100755 --- a/engine/license.txt +++ b/engine/license.txt @@ -3,7 +3,7 @@ * * An open source PHP framework for web developers who like to break the rules * - * Version: 1.3.3040 + * Version: 1.3.3041 * * This product is released under the MIT License (MIT) * diff --git a/engine/tg_helpers/file_helper.php b/engine/tg_helpers/file_helper.php index 1b049d5a..fb10de28 100644 --- a/engine/tg_helpers/file_helper.php +++ b/engine/tg_helpers/file_helper.php @@ -15,29 +15,64 @@ public function upload($config) { $make_rand_name = false; } - $new_file_name = ($make_rand_name === true) ? make_rand_str(10) : $target_file['name']; + //init $new_file_name variable (the name of the uploaded file) + if($make_rand_name == true) { + $file_name_without_extension = strtolower(make_rand_str(10)); - if ($make_rand_name === true) { //add file extension onto rand file name - $bits = explode('.', $target_file['name']); - $file_extension = '.'.$bits[count($bits)-1]; + $file_info = return_file_info($target_file['name']); + $file_extension = $file_info['file_extension']; + $new_file_name = $file_name_without_extension.$file_extension; + } else { + //get the file name and extension + $file_info = return_file_info($target_file['name']); + $file_name = $file_info['file_name']; + $file_extension = $file_info['file_extension']; - $new_file_name = str_replace($file_extension, '', $new_file_name); - $new_file_name = ltrim(trim(filter_var($new_file_name, FILTER_SANITIZE_STRING))); - $new_file_name.= $file_extension; + //remove dangerous characters from the file name + $file_name = url_title($file_name); + $file_name_without_extension = str_replace('-', '_', $file_name); + $new_file_name = $file_name_without_extension.$file_extension; } - //make sure the destination folder exists $target_destination = '../public/'.$destination; - if (is_dir($target_destination)) { + try { + //make sure the destination folder exists + if (!is_dir($target_destination)) { + $error_msg = 'Invalid directory'; + if (strlen($target_destination)>0) { + $error_msg.= ': \''.$target_destination.'\' (string '.strlen($target_destination).')'; + } + throw new Exception($error_msg); + } + //upload the temp file to the destination $new_file_path = $target_destination.'/'.$new_file_name; - move_uploaded_file($target_file['tmp_name'], $new_file_path); - } else { - die('ERROR: Unable to find target file destination: $destination'); + $i = 2; + while(file_exists($new_file_path)) { + $new_file_name = $file_name_without_extension.'_'.$i.$file_extension; + $new_file_path = $target_destination.'/'.$new_file_name; + $i++; + } + + move_uploaded_file($target_file['tmp_name'], $new_file_path); + + //create an array to store file information + $file_info = array(); + $file_info['file_name'] = $new_file_name; + $file_info['file_path'] = $new_file_path; + $file_info['file_type'] = $target_file['type']; + $file_info['file_size'] = $target_file['size']; + return $file_info; + + } catch (Exception $e) { + // Code to handle the exception + echo "An exception occurred: " . $e->getMessage(); + die(); } + } } \ No newline at end of file diff --git a/engine/tg_helpers/img_helper.php b/engine/tg_helpers/img_helper.php index b3e6b012..c56a79e7 100644 --- a/engine/tg_helpers/img_helper.php +++ b/engine/tg_helpers/img_helper.php @@ -3,26 +3,30 @@ class Img_helper { public function upload($data) { - if (!isset($data['upload_to_module'])) { - $data['upload_to_module'] = false; + //declare all inbound variables + $destination = $data['destination'] ?? ''; + $max_width = $data['max_width'] ?? 450; + $max_height = $data['max_height'] ?? 450; + $thumbnail_dir = $data['thumbnail_dir'] ?? ''; + $thumbnail_max_width = $data['thumbnail_max_width'] ?? 0; + $thumbnail_max_height = $data['thumbnail_max_height'] ?? 0; + $upload_to_module = $data['upload_to_module'] ?? false; + $make_rand_name = $data['make_rand_name'] ?? false; + + //check for valid image + $userfile = array_keys($_FILES)[0]; + + if ($_FILES[$userfile]['error'] !== UPLOAD_ERR_OK) { + throw new Exception("An error occurred while uploading the file. Error code: " . $_FILES[$userfile]['error']); } - //check for valid image width and mime type - $userfile = array_keys($_FILES)[0]; $target_file = $_FILES[$userfile]; $dimension_data = getimagesize($target_file['tmp_name']); $image_width = $dimension_data[0]; if (!is_numeric($image_width)) { - die('ERROR: non numeric image width'); - } - - $content_type = mime_content_type($target_file['tmp_name']); - - $str = substr($content_type, 0, 6); - if ($str !== 'image/') { - die('ERROR: not an image.'); + throw new Exception("ERROR: non numeric image width"); } $tmp_name = $target_file['tmp_name']; @@ -30,78 +34,119 @@ public function upload($data) { $data['tmp_file_width'] = $data['image']->getWidth(); $data['tmp_file_height'] = $data['image']->getHeight(); - if ($data['upload_to_module'] == true) { - $target_module = (isset($data['targetModule']) ? $data['targetModule'] : segment(1)); - $data['filename'] = '../modules/'.$target_module.'/assets/'.$data['destination'].'/'.$target_file['name']; + //init $new_file_name variable (the name of the uploaded file) + if($make_rand_name == true) { + $file_name_without_extension = strtolower(make_rand_str(10)); + + //add file extension onto rand file name + $file_info = return_file_info($target_file['name']); + $file_extension = $file_info['file_extension']; + $new_file_name = $file_name_without_extension.$file_extension; } else { - $data['filename'] = '../public/'.$data['destination'].'/'.$target_file['name']; + //get the file name and extension + $file_info = return_file_info($target_file['name']); + $file_name = $file_info['file_name']; + $file_extension = $file_info['file_extension']; + + //remove dangerous characters from the file name + $file_name = url_title($file_name); + $file_name_without_extension = str_replace('-', '_', $file_name); + $new_file_name = $file_name_without_extension.$file_extension; } - if (!isset($data['max_width'])) { - $data['max_width'] = NULL; + //set the target destination directory + if ($upload_to_module == true) { + $target_module = (isset($data['targetModule']) ? $data['targetModule'] : segment(1)); + $target_destination = '../modules/'.$target_module.'/assets/'.$data['destination']; + } else { + $target_destination = '../public/'.$data['destination']; } - if (!isset($data['max_height'])) { - $data['max_height'] = NULL; - } + try { + //make sure the destination folder exists + if (!is_dir($target_destination)) { + $error_msg = 'Invalid directory'; + if (strlen($target_destination)>0) { + $error_msg.= ': \''.$target_destination.'\' (string '.strlen($target_destination).')'; + } + throw new Exception($error_msg); + } + + //upload the temp file to the destination + $new_file_path = $target_destination.'/'.$new_file_name; + + $i = 2; + while(file_exists($new_file_path)) { + $new_file_name = $file_name_without_extension.'_'.$i.$file_extension; + $new_file_path = $target_destination.'/'.$new_file_name; + $i++; + } - $this->save_that_pic($data); - - //rock the thumbnail - if ((isset($data['thumbnail_max_width'])) && (isset($data['thumbnail_max_height'])) && (isset($data['thumbnail_dir']))) { - $ditch = $data['destination']; - $replace = $data['thumbnail_dir']; - $data['filename'] = str_replace($ditch, $replace, $data['filename']); - $data['max_width'] = $data['thumbnail_max_width']; - $data['max_height'] = $data['thumbnail_max_height']; + $data['new_file_path'] = $new_file_path; $this->save_that_pic($data); - } - } + + //create an array to store file information + $file_info = array(); + $file_info['file_name'] = $new_file_name; + $file_info['file_path'] = $new_file_path; + $file_info['file_type'] = $target_file['type']; + $file_info['file_size'] = $target_file['size']; + + //deal with the thumbnail + if (($thumbnail_max_width>0) && ($thumbnail_max_height>0) && ($thumbnail_dir !== '')) { + $ditch = $destination; + $replace = $thumbnail_dir; + $data['new_file_path'] = str_replace($ditch, $replace, $data['new_file_path']); + $data['max_width'] = $thumbnail_max_width; + $data['max_height'] = $thumbnail_max_height; + $this->save_that_pic($data); + $file_info['thumbnail_path'] = $data['new_file_path']; + } - private function save_that_pic($data) { - extract($data); - $reduce_width = false; - $reduce_height = false; + return $file_info; - if (!isset($data['compression'])) { - $compression = 100; - } else { - $compression = $data['compression']; + } catch (Exception $e) { + // Code to handle the exception + echo "An exception occurred: " . $e->getMessage(); + die(); } - if (!isset($data['permissions'])) { - $permissions = 775; - } else { - $permissions = $data['permissions']; - } + } - //do we need to resize the picture? - if ((isset($max_width)) && ($tmp_file_width>$max_width)) { - $reduce_width = true; + private function save_that_pic($data) { + $new_file_path = $data['new_file_path'] ?? ''; + $compression = $data['compression'] ?? 100; + $permissions = $data['permissions'] ?? 775; + $max_width = $data['max_width'] ?? 0; + $max_height = $data['max_height'] ?? 0; + $tmp_file_width = $data['tmp_file_width'] ?? 0; + $tmp_file_height = $data['tmp_file_height'] ?? 0; + $image = $data['image']; + + if (($max_width>0 && ($tmp_file_width > $max_width)) || ($max_height>0 && ($tmp_file_height > $max_height))) { + + //calculate if oversize amount is greater with respect to width or height... $resize_factor_w = $tmp_file_width / $max_width; - } - - if ((isset($max_height)) && ($tmp_file_width>$max_height)) { - $reduce_height = true; $resize_factor_h = $tmp_file_height / $max_height; - } - if ((isset($resize_factor_w)) && (isset($resize_factor_h))) { if ($resize_factor_w > $resize_factor_h) { $reduce_height = false; + $reduce_width = true; } else { + $reduce_height = true; $reduce_width = false; } - } - //either do the height resize or the width resize - never both - if ($reduce_width == true) { - $image->resizeToWidth($max_width); - } elseif($reduce_height == true) { - $image->resizeToHeight($max_height); + //either do the height resize or the width resize - never both + if ($reduce_width == true) { + $image->resizeToWidth($max_width); + } elseif($reduce_height == true) { + $image->resizeToHeight($max_height); + } + } - $image->save($filename, $compression); + $image->save($new_file_path, $compression); } } \ No newline at end of file diff --git a/engine/tg_helpers/url_helper.php b/engine/tg_helpers/url_helper.php index c5ce0198..ad677e9b 100644 --- a/engine/tg_helpers/url_helper.php +++ b/engine/tg_helpers/url_helper.php @@ -94,11 +94,20 @@ function url_title($value, $transliteration = true) { } $slug = html_entity_decode($value, ENT_QUOTES, 'UTF-8'); $slug = preg_replace('~[^\pL\d]+~u', '-', $slug); - $slug = trim($slug, '-'); + $slug = trim($slug, '- '); $slug = strtolower($slug); return $slug; } +function return_file_info($file_string) { + // Get the file extension + $file_extension = pathinfo($file_string, PATHINFO_EXTENSION); + // Get the file name without the extension + $file_name = str_replace("." . $file_extension, "", $file_string); + // Return an array containing the file name and file extension + return array("file_name" => $file_name, "file_extension" => "." . $file_extension); +} + function api_auth() { //find out where the api.json file lives $validation_complete = false; diff --git a/license.txt b/license.txt index ebff4325..32dc4035 100755 --- a/license.txt +++ b/license.txt @@ -3,7 +3,7 @@ * * An open source PHP framework for web developers who like to break the rules * - * Version: 1.3.3040 + * Version: 1.3.3041 * * This product is released under the MIT License (MIT) * From a88ef1d15aa4499ef6cd553e7f0486d0eaa1c23c Mon Sep 17 00:00:00 2001 From: Simon Field Date: Sat, 21 Jan 2023 15:02:11 +1100 Subject: [PATCH 065/100] Allow save to module - No breaking changes - Upload file to module --- engine/tg_helpers/file_helper.php | 44 ++++++++++++++++++------------- 1 file changed, 26 insertions(+), 18 deletions(-) diff --git a/engine/tg_helpers/file_helper.php b/engine/tg_helpers/file_helper.php index fb10de28..3744e385 100644 --- a/engine/tg_helpers/file_helper.php +++ b/engine/tg_helpers/file_helper.php @@ -2,7 +2,12 @@ class File_helper { public function upload($config) { - extract($config); + + //declare all inbound variables + $destination = $config['destination'] ?? NULL; + $target_module = $config['target_module'] ?? segment(1); + $upload_to_module = $config['upload_to_module'] ?? false; + $make_rand_name = $config['make_rand_name'] ?? false; if (!isset($destination)) { die('ERROR: upload requires inclusion of \'destination\' property. Check documentation for details.'); @@ -11,18 +16,18 @@ public function upload($config) { $userfile = array_keys($_FILES)[0]; $target_file = $_FILES[$userfile]; - if(!isset($make_rand_name)) { + if (!isset($make_rand_name)) { $make_rand_name = false; } //init $new_file_name variable (the name of the uploaded file) - if($make_rand_name == true) { + if ($make_rand_name == true) { $file_name_without_extension = strtolower(make_rand_str(10)); //add file extension onto rand file name $file_info = return_file_info($target_file['name']); $file_extension = $file_info['file_extension']; - $new_file_name = $file_name_without_extension.$file_extension; + $new_file_name = $file_name_without_extension . $file_extension; } else { //get the file name and extension $file_info = return_file_info($target_file['name']); @@ -32,33 +37,39 @@ public function upload($config) { //remove dangerous characters from the file name $file_name = url_title($file_name); $file_name_without_extension = str_replace('-', '_', $file_name); - $new_file_name = $file_name_without_extension.$file_extension; + $new_file_name = $file_name_without_extension . $file_extension; } - $target_destination = '../public/'.$destination; + //set the target destination directory + if ($upload_to_module == true) { + $target_destination = '../modules/' . $target_module . '/assets/' . $destination; + } else { + //add code here to deal with external URLs (AWS, Google Drive, OneDrive, etc...) + $target_destination = $destination; + } try { //make sure the destination folder exists if (!is_dir($target_destination)) { $error_msg = 'Invalid directory'; - if (strlen($target_destination)>0) { - $error_msg.= ': \''.$target_destination.'\' (string '.strlen($target_destination).')'; + if (strlen($target_destination) > 0) { + $error_msg .= ': \'' . $target_destination . '\' (string ' . strlen($target_destination) . ')'; } throw new Exception($error_msg); } //upload the temp file to the destination - $new_file_path = $target_destination.'/'.$new_file_name; + $new_file_path = $target_destination . '/' . $new_file_name; $i = 2; - while(file_exists($new_file_path)) { - $new_file_name = $file_name_without_extension.'_'.$i.$file_extension; - $new_file_path = $target_destination.'/'.$new_file_name; + while (file_exists($new_file_path)) { + $new_file_name = $file_name_without_extension . '_' . $i . $file_extension; + $new_file_path = $target_destination . '/' . $new_file_name; $i++; } - move_uploaded_file($target_file['tmp_name'], $new_file_path); - + move_uploaded_file($target_file['tmp_name'], $new_file_path); + //create an array to store file information $file_info = array(); $file_info['file_name'] = $new_file_name; @@ -66,13 +77,10 @@ public function upload($config) { $file_info['file_type'] = $target_file['type']; $file_info['file_size'] = $target_file['size']; return $file_info; - } catch (Exception $e) { // Code to handle the exception echo "An exception occurred: " . $e->getMessage(); die(); } - } - -} \ No newline at end of file +} From 05bf1dcd8e2247f02611d3630b17be2885668906 Mon Sep 17 00:00:00 2001 From: David Connelly Date: Sat, 21 Jan 2023 16:58:41 +0000 Subject: [PATCH 066/100] Improve file uploader (thank you, Dafa!) and improved CSRF protection - see release notes for more info. --- engine/Core.php | 22 ------------ engine/tg_helpers/validation_helper.php | 45 +++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 22 deletions(-) diff --git a/engine/Core.php b/engine/Core.php index 616d2290..ec5ca833 100644 --- a/engine/Core.php +++ b/engine/Core.php @@ -202,7 +202,6 @@ private function serve_controller() { } if (method_exists($this->current_controller, $this->current_method)) { - $this->csrf_protect(); $target_method = $this->current_method; $this->current_controller = new $this->current_controller($this->current_module); $this->current_controller->$target_method($this->current_value); @@ -211,27 +210,6 @@ private function serve_controller() { } } - private function csrf_protect() { - if (isset($_POST['submit'])) { - //make sure they have posted csrf_token - if (!isset($_POST['csrf_token'])) { - $this->csrf_block_request(); - } else { - $result = password_verify(session_id(), $_POST['csrf_token']); - if ($result == false) { - $this->csrf_block_request(); - } - - unset($_POST['csrf_token']); - } - } - } - - private function csrf_block_request() { - header("location: ".BASE_URL); - die(); - } - private function attempt_init_child_controller($controller_path) { $bits = explode('-', $this->current_controller); diff --git a/engine/tg_helpers/validation_helper.php b/engine/tg_helpers/validation_helper.php index b6dedb27..155e3784 100644 --- a/engine/tg_helpers/validation_helper.php +++ b/engine/tg_helpers/validation_helper.php @@ -6,6 +6,8 @@ class Validation_helper { public function set_rules($key, $label, $rules) { + $this->csrf_protect(); + if ((!isset($_POST[$key])) && (isset($_FILES[$key]))) { if (!isset($_POST[$key])) { @@ -558,6 +560,29 @@ private function _get_test_name($test_to_run) { } private function validate_file($key, $label, $rules) { + if(!isset($_FILES[$key])) { + $this->handle_missing_file_error($key, $label); + return; + } + if ($_FILES[$key]['name'] == '') { + $this->handle_empty_file_error($key, $label); + return; + } + $this->run_file_validation($key, $rules); + } + + private function handle_missing_file_error($key, $label) { + $error_msg = 'You are required to select a file.'; + $this->form_submission_errors[$key][] = $error_msg; + } + + private function handle_empty_file_error($key, $label) { + $error_msg = 'You did not select a file.'; + $this->form_submission_errors[$key][] = $error_msg; + } + + private function run_file_validation($key, $rules) { + // file validation logic here require_once('file_validation_helper.php'); } @@ -611,6 +636,25 @@ public function url_segment($num) { return $value; } + private function csrf_protect() { + //make sure they have posted csrf_token + if (!isset($_POST['csrf_token'])) { + $this->csrf_block_request(); + } else { + $result = password_verify(session_id(), $_POST['csrf_token']); + if ($result == false) { + $this->csrf_block_request(); + } + + unset($_POST['csrf_token']); + } + } + + private function csrf_block_request() { + header("location: ".BASE_URL); + die(); + } + } function validation_errors($opening_html=NULL, $closing_html=NULL) { @@ -656,4 +700,5 @@ function validation_errors($opening_html=NULL, $closing_html=NULL) { } echo $validation_err_str; } + } \ No newline at end of file From f8cb115475247f59d58722b385501816f0392371 Mon Sep 17 00:00:00 2001 From: David Connelly Date: Sat, 21 Jan 2023 17:01:46 +0000 Subject: [PATCH 067/100] Updated license - now at v1.3.3042. See release notes for more details. --- engine/license.txt | 2 +- license.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/engine/license.txt b/engine/license.txt index 32dc4035..04e3c87f 100755 --- a/engine/license.txt +++ b/engine/license.txt @@ -3,7 +3,7 @@ * * An open source PHP framework for web developers who like to break the rules * - * Version: 1.3.3041 + * Version: 1.3.3042 * * This product is released under the MIT License (MIT) * diff --git a/license.txt b/license.txt index 32dc4035..04e3c87f 100755 --- a/license.txt +++ b/license.txt @@ -3,7 +3,7 @@ * * An open source PHP framework for web developers who like to break the rules * - * Version: 1.3.3041 + * Version: 1.3.3042 * * This product is released under the MIT License (MIT) * From 423791fa71b3a21cfca5a01e4f0421aa61a69485 Mon Sep 17 00:00:00 2001 From: David Connelly Date: Mon, 23 Jan 2023 07:47:45 +0000 Subject: [PATCH 068/100] correction to csrf_protection on validation helper --- engine/license.txt | 2 +- engine/tg_helpers/validation_helper.php | 4 ++-- license.txt | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/engine/license.txt b/engine/license.txt index 04e3c87f..6879d472 100755 --- a/engine/license.txt +++ b/engine/license.txt @@ -3,7 +3,7 @@ * * An open source PHP framework for web developers who like to break the rules * - * Version: 1.3.3042 + * Version: 1.3.3043 * * This product is released under the MIT License (MIT) * diff --git a/engine/tg_helpers/validation_helper.php b/engine/tg_helpers/validation_helper.php index 155e3784..27773215 100644 --- a/engine/tg_helpers/validation_helper.php +++ b/engine/tg_helpers/validation_helper.php @@ -6,8 +6,6 @@ class Validation_helper { public function set_rules($key, $label, $rules) { - $this->csrf_protect(); - if ((!isset($_POST[$key])) && (isset($_FILES[$key]))) { if (!isset($_POST[$key])) { @@ -89,6 +87,8 @@ private function run_validation_test($validation_data, $rules=null) { public function run($validation_array=null) { + $this->csrf_protect(); + if (isset($_SESSION['form_submission_errors'])) { unset($_SESSION['form_submission_errors']); } diff --git a/license.txt b/license.txt index 04e3c87f..6879d472 100755 --- a/license.txt +++ b/license.txt @@ -3,7 +3,7 @@ * * An open source PHP framework for web developers who like to break the rules * - * Version: 1.3.3042 + * Version: 1.3.3043 * * This product is released under the MIT License (MIT) * From 6d93faf50ec559d31e562a33a618a9b39eecb1c5 Mon Sep 17 00:00:00 2001 From: David Connelly Date: Wed, 1 Feb 2023 16:02:28 +0000 Subject: [PATCH 069/100] updated default homepage message --- modules/welcome/views/welcome.php | 39 +++++++++++++++++++++++++++---- 1 file changed, 34 insertions(+), 5 deletions(-) diff --git a/modules/welcome/views/welcome.php b/modules/welcome/views/welcome.php index 3a6ef893..36d5c093 100755 --- a/modules/welcome/views/welcome.php +++ b/modules/welcome/views/welcome.php @@ -6,11 +6,13 @@
    -

    Congratulations

    -

    It Totally Works!

    -

    This page is being generated dynamically by the Trongate Framework.

    -

    To edit this page, go to: modules/welcome/views/welcome.php

    -

    To explore the documentation go to http://www.trongate.io/documentation

    +

    It Totally Works!

    +

    Welcome to Trongate - the revolutionary new framework for developers who love pure PHP.

    + +

    To explore the documentation go to https://trongate.io/docs

    + +

    Please give Trongate a star on GitHub. If Trongate becomes a top ten PHP framework, it'll be the most electrifying event in the history of web development! Join the revolution! Be part of something amazing. Together, we SHALL make PHP great again!

    +
    \ No newline at end of file From 0805a81528e2cd94dd1eb916038bf3ca32ff6dc3 Mon Sep 17 00:00:00 2001 From: David Connelly Date: Sat, 11 Feb 2023 19:15:11 +0000 Subject: [PATCH 070/100] correction to filter_string() function - previously it was not preserving new lines when data was submitted from a textarea --- engine/tg_helpers/form_helper.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/engine/tg_helpers/form_helper.php b/engine/tg_helpers/form_helper.php index 8b6d81c1..ba98954d 100644 --- a/engine/tg_helpers/form_helper.php +++ b/engine/tg_helpers/form_helper.php @@ -317,8 +317,8 @@ function filter_string($string, $allowed_tags=[]) { // Apply XSS filtering $string = htmlspecialchars($string); - // Convert double spaces to single spaces - $string = preg_replace('/\s+/', ' ', $string); + // Convert multiple consecutive whitespaces to a single space, except for line breaks + $string = preg_replace('/[^\S\r\n]+/', ' ', $string); // Trim leading and trailing white space $string = trim($string); From eea100c56db3d5738e3bc3c2627357d14e6a0eb2 Mon Sep 17 00:00:00 2001 From: David Connelly Date: Sat, 11 Feb 2023 19:21:45 +0000 Subject: [PATCH 071/100] removed unique from validation helper --- engine/tg_helpers/validation_helper.php | 51 ------------------------- 1 file changed, 51 deletions(-) diff --git a/engine/tg_helpers/validation_helper.php b/engine/tg_helpers/validation_helper.php index 27773215..abeb416a 100644 --- a/engine/tg_helpers/validation_helper.php +++ b/engine/tg_helpers/validation_helper.php @@ -74,10 +74,6 @@ private function run_validation_test($validation_data, $rules=null) { case 'valid_time': $this->valid_time($validation_data); break; - case 'unique': - $inner_value = $validation_data['inner_value'] ?? 0; - $this->unique($validation_data, $inner_value); - break; default: $this->run_special_test($validation_data); break; @@ -387,50 +383,6 @@ private function max_length($key, $label, $posted_value, $inner_value) { } } - private function unique($key, $label, $posted_value, $inner_value=null) { - - if ($posted_value == '') { - return; - } - - $forbidden_values[] = $posted_value; - - $bits = explode(',', $inner_value); - if (count($bits) == 2) { - $allowed_id = $bits[0]; - $table_name = $bits[1]; - } else { - $allowed_id = $inner_value; - $table_name = SEGMENTS[1]; - } - - settype($allowed_id, 'int'); - - require_once(__DIR__.'/../Model.php'); - $model = new Model(); - - $sql = 'select * from '.$table_name; - $rows = $model->query($sql, 'object'); - - $forbidden_values[] = trim(strip_tags($posted_value)); - $forbidden_values[] = preg_replace('/\s+/', ' ', $posted_value); - - if (!defined('ALLOW_SPECIAL_CHARACTERS')) { - //filter out potentially malicious characters - $forbidden_values[] = remove_special_characters($posted_value); - } - - foreach($rows as $row) { - $row_id = $row->id; - $row_target_value = $row->$key; - if ((in_array($row_target_value, $forbidden_values)) && ($row->id !== $allowed_id)) { - $this->form_submission_errors[$key][] = 'The ' . $label . ' that you submitted is already on our system.'; - break; - } - } - } - - private function greater_than($key, $label, $posted_value, $inner_value) { if (((is_numeric($_POST[$key])) && ($_POST[$key]<=$inner_value)) && ($posted_value !== '')) { @@ -523,9 +475,6 @@ private function run_special_test($validation_data) { case 'max_length': $this->max_length($key, $label, $posted_value, $inner_value); break; - case 'unique': - $this->unique($key, $label, $posted_value, $inner_value); - break; case 'greater_than': $this->greater_than($key, $label, $posted_value, $inner_value); break; From 72cbf0cb1deae2b5a4fcd473c6fe33d27a73341b Mon Sep 17 00:00:00 2001 From: David Connelly Date: Sat, 11 Feb 2023 20:06:25 +0000 Subject: [PATCH 072/100] Chanaged the way the Model class gets loaded. This means that it is now possible to run Trongate web applications entirely without a database of any kind --- engine/Dynamic_properties.php | 10 ++++++++-- engine/Trongate.php | 6 +----- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/engine/Dynamic_properties.php b/engine/Dynamic_properties.php index 4165c0d8..5c8b39bc 100644 --- a/engine/Dynamic_properties.php +++ b/engine/Dynamic_properties.php @@ -5,10 +5,16 @@ trait Dynamic_properties { public function __set(string $key, object $value): void { $this->attributes[$key] = $value; } - + public function __get(string $key) { - if (!isset($this->attributes[$key])) { + if ($key === 'model') { + if (!isset($this->model)) { + require_once 'Model.php'; + $this->model = new Model($this->module_name); + } + return $this->model; + } elseif (!isset($this->attributes[$key])) { $class_name = ucfirst($key); $this->$key = new $key; } diff --git a/engine/Trongate.php b/engine/Trongate.php index 84731b3c..666733be 100755 --- a/engine/Trongate.php +++ b/engine/Trongate.php @@ -3,8 +3,8 @@ class Trongate { use Dynamic_properties; + private $model; protected $modules; - protected $model; protected $url; protected $module_name; protected $parent_module = ''; @@ -13,10 +13,6 @@ class Trongate { public function __construct($module_name=NULL) { $this->module_name = $module_name; $this->modules = new Modules; - - //load the model class - require_once 'Model.php'; - $this->model = new Model($module_name); } public function load($helper) { From feac5232b2d735aa751363c9ec83fde2dc9fb768 Mon Sep 17 00:00:00 2001 From: David Connelly Date: Sun, 12 Feb 2023 17:33:28 +0000 Subject: [PATCH 073/100] changed license number to 3.3.3044 --- engine/license.txt | 2 +- license.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/engine/license.txt b/engine/license.txt index 6879d472..1e76ce3b 100755 --- a/engine/license.txt +++ b/engine/license.txt @@ -3,7 +3,7 @@ * * An open source PHP framework for web developers who like to break the rules * - * Version: 1.3.3043 + * Version: 1.3.3044 * * This product is released under the MIT License (MIT) * diff --git a/license.txt b/license.txt index 6879d472..1e76ce3b 100755 --- a/license.txt +++ b/license.txt @@ -3,7 +3,7 @@ * * An open source PHP framework for web developers who like to break the rules * - * Version: 1.3.3043 + * Version: 1.3.3044 * * This product is released under the MIT License (MIT) * From 9007578c8bf944ff99759599097bd30e08293a4e Mon Sep 17 00:00:00 2001 From: David Connelly Date: Wed, 15 Mar 2023 08:23:42 +0000 Subject: [PATCH 074/100] Correction to valid_email method --- engine/tg_helpers/validation_helper.php | 46 ++++++++++++++----------- 1 file changed, 25 insertions(+), 21 deletions(-) diff --git a/engine/tg_helpers/validation_helper.php b/engine/tg_helpers/validation_helper.php index abeb416a..ad539626 100644 --- a/engine/tg_helpers/validation_helper.php +++ b/engine/tg_helpers/validation_helper.php @@ -402,32 +402,36 @@ private function less_than($key, $label, $posted_value, $inner_value) { private function valid_email($validation_data) { extract($validation_data); - if ((!filter_var($posted_value, FILTER_VALIDATE_EMAIL)) && ($posted_value !== '')) { - $this->form_submission_errors[$key][] = 'The '.$label.' field must contain a valid email address.'; - return; - } + if ($posted_value !== '') { - // Check if the email address contains an @ symbol and a valid domain name - $at_pos = strpos($posted_value, '@'); - if ($at_pos === false || $at_pos === 0) { - $this->form_submission_errors[$key][] = 'The '.$label.' is not properly formatted.'; - return; - } + if (!filter_var($posted_value, FILTER_VALIDATE_EMAIL)) { + $this->form_submission_errors[$key][] = 'The '.$label.' field must contain a valid email address.'; + return; + } - // Make sure the email address is not too long - if (strlen($posted_value) > 254) { - $this->form_submission_errors[$key][] = 'The '.$label.' is too long.'; - return; - } + // Check if the email address contains an @ symbol and a valid domain name + $at_pos = strpos($posted_value, '@'); + if ($at_pos === false || $at_pos === 0) { + $this->form_submission_errors[$key][] = 'The '.$label.' is not properly formatted.'; + return; + } - // Check if the internet is available - if($sock = @fsockopen('www.google.com', 80)) { - fclose($sock); - $domain_name = substr($posted_value, $at_pos + 1); - if (!checkdnsrr($domain_name, 'MX')) { - $this->form_submission_errors[$key][] = 'The '.$label.' field contains an invalid domain name'; + // Make sure the email address is not too long + if (strlen($posted_value) > 254) { + $this->form_submission_errors[$key][] = 'The '.$label.' is too long.'; return; } + + // Check if the internet is available + if($sock = @fsockopen('www.google.com', 80)) { + fclose($sock); + $domain_name = substr($posted_value, $at_pos + 1); + if (!checkdnsrr($domain_name, 'MX')) { + $this->form_submission_errors[$key][] = 'The '.$label.' field contains an invalid domain name'; + return; + } + } + } } From ccb68d9470cf8ce88dd46f60ab8b084cb105e39a Mon Sep 17 00:00:00 2001 From: chr1974 Date: Fri, 31 Mar 2023 23:02:22 +0200 Subject: [PATCH 075/100] No need for an else clause, just initialize $user_level before if statement. --- .../controllers/Trongate_user_levels.php | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/modules/trongate_users/trongate_user_levels/controllers/Trongate_user_levels.php b/modules/trongate_users/trongate_user_levels/controllers/Trongate_user_levels.php index 89a5f5de..48849d1a 100755 --- a/modules/trongate_users/trongate_user_levels/controllers/Trongate_user_levels.php +++ b/modules/trongate_users/trongate_user_levels/controllers/Trongate_user_levels.php @@ -18,12 +18,11 @@ function _get_user_level($user_id) { $data['user_id'] = $user_id; $result = $this->model->query_bind($sql, $data, 'array'); - + + $user_level = ''; if (isset($result[0])) { $user_level = $result[0]['level_title']; - } else { - $user_level = ''; - } + } return $user_level; } From e9ee4a10eb334de0e555e2461de24c81883abf33 Mon Sep 17 00:00:00 2001 From: chr1974 Date: Sat, 1 Apr 2023 15:34:47 +0200 Subject: [PATCH 076/100] Changed to oneliner, to get rid of if else clause --- .../controllers/Trongate_user_levels.php | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/modules/trongate_users/trongate_user_levels/controllers/Trongate_user_levels.php b/modules/trongate_users/trongate_user_levels/controllers/Trongate_user_levels.php index 48849d1a..d5d84a73 100755 --- a/modules/trongate_users/trongate_user_levels/controllers/Trongate_user_levels.php +++ b/modules/trongate_users/trongate_user_levels/controllers/Trongate_user_levels.php @@ -19,10 +19,7 @@ function _get_user_level($user_id) { $data['user_id'] = $user_id; $result = $this->model->query_bind($sql, $data, 'array'); - $user_level = ''; - if (isset($result[0])) { - $user_level = $result[0]['level_title']; - } + $user_level = $result[0]['user_level'] ?? ''; return $user_level; } From aa22e3551737b68c337930499e77cd4c2df60832 Mon Sep 17 00:00:00 2001 From: chr1974 Date: Sat, 1 Apr 2023 15:56:22 +0200 Subject: [PATCH 077/100] fixed wrong var string in array --- .../trongate_user_levels/controllers/Trongate_user_levels.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/trongate_users/trongate_user_levels/controllers/Trongate_user_levels.php b/modules/trongate_users/trongate_user_levels/controllers/Trongate_user_levels.php index d5d84a73..70fb28c8 100755 --- a/modules/trongate_users/trongate_user_levels/controllers/Trongate_user_levels.php +++ b/modules/trongate_users/trongate_user_levels/controllers/Trongate_user_levels.php @@ -19,7 +19,7 @@ function _get_user_level($user_id) { $data['user_id'] = $user_id; $result = $this->model->query_bind($sql, $data, 'array'); - $user_level = $result[0]['user_level'] ?? ''; + $user_level = $result[0]['level_title'] ?? ''; return $user_level; } From f49c2140d7ac04df443100fedd57b0ca7ae87935 Mon Sep 17 00:00:00 2001 From: David Connelly Date: Mon, 3 Apr 2023 13:32:56 +0100 Subject: [PATCH 078/100] REBUILT API MANAGER --- engine/Api.php | 1507 +++--------------------- engine/Core.php | 42 +- engine/Standard_endpoints.php | 1103 +++++++++++++++++ engine/tg_helpers/url_helper.php | 14 +- engine/views/api_explorer.php | 642 +--------- engine/views/api_explorer_js.php | 271 +++++ engine/views/api_explorer_modal_js.php | 781 ++++++++++++ engine/views/api_explorer_style.php | 382 ++++++ engine/views/highlight_errors.txt | 2 +- 9 files changed, 2785 insertions(+), 1959 deletions(-) create mode 100644 engine/Standard_endpoints.php create mode 100644 engine/views/api_explorer_js.php create mode 100644 engine/views/api_explorer_modal_js.php create mode 100644 engine/views/api_explorer_style.php diff --git a/engine/Api.php b/engine/Api.php index f838bba5..641697f8 100755 --- a/engine/Api.php +++ b/engine/Api.php @@ -1,1050 +1,42 @@ _make_sure_table_exists($module_name); - $module_endpoints = $this->_fetch_endpoints($module_name); - - if ($_SERVER['REQUEST_METHOD'] == 'GET') { - $this->_process_get_with_get($module_name, $module_endpoints); - } else { - $this->_process_get_with_post($module_name, $module_endpoints); - } - - } - - function _process_get_with_get($module_name, $module_endpoints) { - - $update_id = segment(4); - if (is_numeric($update_id)) { - $this->_find_one($module_name, $module_endpoints, $update_id); - return; - } - - $endpoint_name = 'Get'; - $token_validation_data['endpoint'] = $endpoint_name; - $token_validation_data['module_name'] = $module_name; - $token_validation_data['module_endpoints'] = $module_endpoints; - $input['token'] = $this->_validate_token($token_validation_data); - - $output['token'] = $input['token']; - - $params = $this->_get_params_from_url(4); - $sql = 'select * from '.$module_name; - - //attempt invoke 'before' hook - $input['params'] = $params; - $input['module_name'] = $module_name; - $input['endpoint'] = $endpoint_name; - - $target_endpoint = $module_endpoints[$input['endpoint']]; - - $input = $this->_attempt_invoke_before_hook($input, $module_endpoints, $target_endpoint); - extract($input); - - $num_params = count($params); - - if ($num_params < 1) { - $rows = $this->model->get('id', $module_name); - $output['body'] = json_encode($rows); - $output['code'] = 200; - $output['module_name'] = $module_name; - - $output = $this->_attempt_invoke_after_hook($output, $module_endpoints, $target_endpoint); - - $code = $output['code']; - http_response_code($code); - echo $output['body']; - - } else { - //params were submitted via URL - $sql = 'select * from '.$module_name; - $params = json_encode($params); - $params = ltrim($params); - $params = json_decode($params); - $params = get_object_vars($params); - $query_info = $this->_add_params_to_query($module_name, $sql, $params); - - $sql = $query_info['sql']; - $data = $query_info['data']; - - if (count($data)<1) { - $rows = $this->model->query($sql, 'object'); - } else { - $rows = $this->model->query_bind($sql, $data, 'object'); - } - - $output['body'] = json_encode($rows); - $output['code'] = 200; - $output['module_name'] = $module_name; - - $output = $this->_attempt_invoke_after_hook($output, $module_endpoints, $target_endpoint); - - $code = $output['code']; - http_response_code($code); - echo $output['body']; - } - - } - - function _process_get_with_post($module_name, $module_endpoints) { - - $endpoint_name = 'Get By Post'; - $token_validation_data['endpoint'] = $endpoint_name; - $token_validation_data['module_name'] = $module_name; - $token_validation_data['module_endpoints'] = $module_endpoints; - $input['token'] = $this->_validate_token($token_validation_data); - - $output['token'] = $input['token']; - - - //get posted params - $post = file_get_contents('php://input'); - $decoded = json_decode($post, true); - - if (!isset($decoded)) { - $decoded = []; - } - - if (count($decoded)>0) { - $params = $this->_get_params_from_post($decoded); - } else { - $params = []; - } - - $sql = 'select * from '.$module_name; - - //attempt invoke 'before' hook - $input['params'] = $params; - $input['module_name'] = $module_name; - $input['endpoint'] = $endpoint_name; - - $target_endpoint = $module_endpoints[$input['endpoint']]; - $input = $this->_attempt_invoke_before_hook($input, $module_endpoints, $target_endpoint); - extract($input); - - $num_params = count($params); - - if ($num_params < 1) { - - $rows = $this->model->get('id', $module_name); - $output['body'] = json_encode($rows); - $output['code'] = 200; - $output['module_name'] = $module_name; - - $output = $this->_attempt_invoke_after_hook($output, $module_endpoints, $target_endpoint); - - $code = $output['code']; - http_response_code($code); - echo $output['body']; - - } else { - //params were posted - $sql = 'select * from '.$module_name; - $params = json_encode($params); - $params = ltrim($params); - $params = json_decode($params); - $params = get_object_vars($params); - $query_info = $this->_add_params_to_query($module_name, $sql, $params); - - $sql = $query_info['sql']; - $data = $query_info['data']; - - if (count($data)<1) { - $rows = $this->model->query($sql, 'object'); - } else { - $rows = $this->model->query_bind($sql, $data, 'object'); - } - - $output['body'] = json_encode($rows); - $output['code'] = 200; - $output['module_name'] = $module_name; - - $output = $this->_attempt_invoke_after_hook($output, $module_endpoints, $target_endpoint); - - $code = $output['code']; - http_response_code($code); - echo $output['body']; - } - } - - function _validate_token($token_validation_data) { - //make sure the user is allowed to be here, if YES -> return token or true - //if NO -> display not allowed message - - $this->module('trongate_tokens'); - - //attempt to fetch user token from header - $token = (isset($_SERVER['HTTP_TRONGATETOKEN']) ? $_SERVER['HTTP_TRONGATETOKEN'] : false); - - if ((($token == '') || ($token == false)) && (segment(1) !== 'api')) { - //attempt to fetch a token from cookie or session data - $token = $this->trongate_tokens->_attempt_get_valid_token(); - } - - //get an array of ALL the rules for this endpoint - $module_endpoints = $token_validation_data['module_endpoints']; - $endpoint = $token_validation_data['endpoint']; - $target_endpoint = $module_endpoints[$endpoint]; - $endpoint_auth_rules = $this->_fetch_endpoint_auth_rules($target_endpoint); - - if ($endpoint_auth_rules == false) { - $error_msg = 'Endpoint not activated since no authorization rules have been declared.'; - $this->_api_manager_error(412, $error_msg); - } - - if ($endpoint_auth_rules == '*') { - //wide open endpoint - end of the road - if (gettype($token) == 'boolean') { - return true; - } else { - return $token; - } - } - - //attempt fetch user object from the token - $trongate_user = $this->trongate_tokens->_get_user_obj($token); - - if ($trongate_user == false) { - //could not find a user - but do we have a aaa token? - if ($token == false) { - $this->_not_allowed_msg(); - } else { - $result = $this->_test_for_aaa_token($token); - - if ($result == true) { - return $token; - } else { - $this->_not_allowed_msg(); - } - } - } - - //let's go through all of the authoriation rules and attempt to pass ONE of them - - //role based authorization - if (isset($endpoint_auth_rules['roles'])) { - if (in_array($trongate_user->user_level, $endpoint_auth_rules['roles'])) { - return $token; //success! - } - } - - //id based authorization - if (isset($endpoint_auth_rules['userIds'])) { - if (in_array($trongate_user->trongate_user_id, $endpoint_auth_rules['userIds'])) { - return $token; //success! - } - } - - //user id segment authorization - if (isset($endpoint_auth_rules['userIdSegment'])) { - $target_value = segment($endpoint_auth_rules['userIdSegment']); - if ($trongate_user->trongate_user_id == $target_value) { - return $token; //success! - } - } - - //user code segment authorization - if (isset($endpoint_auth_rules['userCodeSegment'])) { - $target_value = segment($endpoint_auth_rules['userCodeSegment']); - if ($trongate_user->trongate_user_code == $target_value) { - return $token; //success! - } - } - - //user owned segment authorization - if (isset($endpoint_auth_rules['userOwnedSegment'])) { - $test_data['module_name'] = $token_validation_data['module_name']; - $test_data['trongate_user_id'] = $trongate_user->trongate_user_id; - $test_data['user_owned_settings'] = $endpoint_auth_rules['userOwnedSegment']; - $result = $this->_run_user_owned_test($test_data); //true or false - - if ($result == true) { - return $token; //success! - } else { - $this->_not_allowed_msg(); - } - } - - //safety net - $this->_not_allowed_msg(); - } - - function _test_for_aaa_token($token) { - $token_obj = $this->model->get_one_where('token', $token, 'trongate_tokens'); - if ($token_obj == false) { - return false; - } else { - - if ($token_obj->code == 'aaa') { - return true; - } else { - return false; - } - - } - } - - function _run_user_owned_test($test_data) { - $column = $test_data['user_owned_settings']['column']; - $value = segment($test_data['user_owned_settings']['segmentNum']); - $target_table = $test_data['module_name']; - - $record = $this->model->get_one_where($column, $value, $target_table); - - if ($record == false) { - return true; //might still be a legit query from a logged in user - } else { - - $result = false; //assume failure - - if (isset($record->trongate_user_id)) { - - $trongate_user_id = $test_data['trongate_user_id']; - - if ($trongate_user_id == $record->trongate_user_id) { - $result = true; - } - - } - - return $result; - - } - - } - - function _which_segment($url_segments, $identifier_str) { - $target_segment = false; - $segments = explode('/', $url_segments); - foreach($segments as $key => $segment) { - if ($segment == $identifier_str) { - $target_segment = $key+1; - } - } - - return $target_segment; - } - - function _find_one($module_name, $module_endpoints, $update_id) { - $endpoint_name = 'Find One'; - $token_validation_data['endpoint'] = $endpoint_name; - $token_validation_data['module_name'] = $module_name; - $token_validation_data['module_endpoints'] = $module_endpoints; - $input['token'] = $this->_validate_token($token_validation_data); - - $output['token'] = $input['token']; + public function explorer() { - //attempt invoke 'before' hook - $input['params'] = []; - $input['module_name'] = $module_name; - $input['endpoint'] = 'Find One'; - - $module_endpoints = $this->_fetch_endpoints($input['module_name']); - $target_endpoint = $module_endpoints[$input['endpoint']]; - $input = $this->_attempt_invoke_before_hook($input, $module_endpoints, $target_endpoint); - extract($input); - - $result = $this->model->get_where($update_id, $module_name); - - $output['body'] = json_encode($result); - $output['code'] = 200; - $output['module_name'] = $module_name; - - $output = $this->_attempt_invoke_after_hook($output, $module_endpoints, $target_endpoint); - - $code = $output['code']; - http_response_code($code); - echo $output['body']; - die(); - } - - function exists() { - - $module_name = segment(3); - $this->_make_sure_table_exists($module_name); - $module_endpoints = $this->_fetch_endpoints($module_name); - - $endpoint_name = 'Exists'; - $token_validation_data['endpoint'] = $endpoint_name; - $token_validation_data['module_name'] = $module_name; - $token_validation_data['module_endpoints'] = $module_endpoints; - $input['token'] = $this->_validate_token($token_validation_data); - - $output['token'] = $input['token']; - - $update_id = segment(4); - - if (!is_numeric($update_id)) { - http_response_code(422); - echo "Non numeric ID"; die(); - } else { - - //attempt invoke 'before' hook - $input['token'] = $input['token']; - $input['params'] = []; - $input['module_name'] = $module_name; - $input['endpoint'] = $endpoint_name; - - $module_endpoints = $this->_fetch_endpoints($input['module_name']); - $target_endpoint = $module_endpoints[$input['endpoint']]; - $input = $this->_attempt_invoke_before_hook($input, $module_endpoints, $target_endpoint); - extract($input); - - $result = $this->model->get_where($update_id, $module_name); - - if ($result == false) { - $result = 'false'; - } else { - $result = 'true'; - } - - $output['body'] = $result; - $output['code'] = 200; - $output['module_name'] = $module_name; - - $output = $this->_attempt_invoke_after_hook($output, $module_endpoints, $target_endpoint); - - $code = $output['code']; - http_response_code($code); - echo $output['body']; - - } - - } - - function count() { - $module_name = segment(3); - $this->_make_sure_table_exists($module_name); - $module_endpoints = $this->_fetch_endpoints($module_name); - - if ($_SERVER['REQUEST_METHOD'] == 'GET') { - $this->_process_count_with_get($module_name, $module_endpoints); - } else { - $this->_process_count_with_post($module_name, $module_endpoints); - } - - } - - function _process_count_with_get($module_name, $module_endpoints) { - - $update_id = segment(4); - if (is_numeric($update_id)) { - $this->_find_one($module_name, $module_endpoints, $update_id); - return; - } - - $endpoint_name = 'Count'; - $token_validation_data['endpoint'] = $endpoint_name; - $token_validation_data['module_name'] = $module_name; - $token_validation_data['module_endpoints'] = $module_endpoints; - $input['token'] = $this->_validate_token($token_validation_data); - - $output['token'] = $input['token']; - - - $params = $this->_get_params_from_url(4); - - $sql = 'select * from '.$module_name; - - //attempt invoke 'before' hook - $input['params'] = $params; - $input['module_name'] = $module_name; - $input['endpoint'] = $endpoint_name; - - $target_endpoint = $module_endpoints[$input['endpoint']]; - - $input = $this->_attempt_invoke_before_hook($input, $module_endpoints, $target_endpoint); - extract($input); - - $num_params = count($params); - - if ($num_params < 1) { - $rows = $this->model->get('id', $module_name); - $output['body'] = count($rows); - $output['code'] = 200; - $output['module_name'] = $module_name; - - $output = $this->_attempt_invoke_after_hook($output, $module_endpoints, $target_endpoint); - - $code = $output['code']; - http_response_code($code); - echo $output['body']; - - } else { - //params were submitted via URL - $sql = 'select * from '.$module_name; - $params = json_encode($params); - $params = ltrim($params); - $params = json_decode($params); - $params = get_object_vars($params); - $query_info = $this->_add_params_to_query($module_name, $sql, $params); - - $sql = $query_info['sql']; - $data = $query_info['data']; - - if (count($data)<1) { - $rows = $this->model->query($sql, 'object'); - } else { - $rows = $this->model->query_bind($sql, $data, 'object'); - } - - $output['body'] = count($rows); - $output['code'] = 200; - $output['module_name'] = $module_name; - - $output = $this->_attempt_invoke_after_hook($output, $module_endpoints, $target_endpoint); - - $code = $output['code']; - http_response_code($code); - echo $output['body']; - } - - } - - function _process_count_with_post($module_name, $module_endpoints) { - - $endpoint_name = 'Count By Post'; - $token_validation_data['endpoint'] = $endpoint_name; - $token_validation_data['module_name'] = $module_name; - $token_validation_data['module_endpoints'] = $module_endpoints; - $input['token'] = $this->_validate_token($token_validation_data); - $output['token'] = $input['token']; - - //get posted params - $post = file_get_contents('php://input'); - $decoded = json_decode($post, true); - - if ($decoded == NULL) { - $this->_api_manager_error(400, 'No posted variables!'); - } - - if (count($decoded)>0) { - $params = $this->_get_params_from_post($decoded); - } else { - $params = []; - } - - $sql = 'select * from '.$module_name; - - //attempt invoke 'before' hook - $input['params'] = $params; - $input['module_name'] = $module_name; - $input['endpoint'] = $endpoint_name; - - $target_endpoint = $module_endpoints[$input['endpoint']]; - $input = $this->_attempt_invoke_before_hook($input, $module_endpoints, $target_endpoint); - extract($input); - - $num_params = count($params); - - if ($num_params < 1) { - - $rows = $this->model->get('id', $module_name); - $output['body'] = count($rows); - $output['code'] = 200; - $output['module_name'] = $module_name; - - $output = $this->_attempt_invoke_after_hook($output, $module_endpoints, $target_endpoint); - - $code = $output['code']; - http_response_code($code); - echo $output['body']; - - } else { - //params were posted - $sql = 'select * from '.$module_name; - $params = json_encode($params); - $params = ltrim($params); - $params = json_decode($params); - $params = get_object_vars($params); - $query_info = $this->_add_params_to_query($module_name, $sql, $params); - - $sql = $query_info['sql']; - $data = $query_info['data']; - - if (count($data)<1) { - $rows = $this->model->query($sql, 'object'); - } else { - $rows = $this->model->query_bind($sql, $data, 'object'); - } - - $output['body'] = count($rows); - $output['code'] = 200; - $output['module_name'] = $module_name; - - $output = $this->_attempt_invoke_after_hook($output, $module_endpoints, $target_endpoint); - - $code = $output['code']; - http_response_code($code); - echo $output['body']; - } - } - - function create() { - - $module_name = segment(3); - $this->_make_sure_table_exists($module_name); - $module_endpoints = $this->_fetch_endpoints($module_name); - - $endpoint_name = 'Create'; - $token_validation_data['endpoint'] = $endpoint_name; - $token_validation_data['module_name'] = $module_name; - $token_validation_data['module_endpoints'] = $module_endpoints; - $input['token'] = $this->_validate_token($token_validation_data); - - $output['token'] = $input['token']; - - $post = file_get_contents('php://input'); - $decoded = json_decode($post, true); - - if (gettype($decoded) !== 'array') { - http_response_code(400); - echo 'No posted values have been received!'; - die(); - } - - if (count($decoded)>0) { - $params = $this->_get_params_from_post($decoded); - } else { - $params = []; - } - - //attempt invoke 'before' hook - $input['params'] = $params; - $input['module_name'] = $module_name; - $input['endpoint'] = 'Create'; - - $module_endpoints = $this->_fetch_endpoints($input['module_name']); - $target_endpoint = $module_endpoints[$input['endpoint']]; - $input = $this->_attempt_invoke_before_hook($input, $module_endpoints, $target_endpoint); - extract($input); - - if (count($params)>0) { - //make sure params are valid - $this->_make_sure_columns_exist($module_name, $params); - $new_id = $this->model->insert($params, $module_name); - $result = $this->model->get_where($new_id, $module_name); - $output['code'] = 200; - $output['body'] = json_encode($result); - } else { - $output['code'] = 422; - $output['body'] = ''; - } - - $output['module_name'] = $module_name; - $output = $this->_attempt_invoke_after_hook($output, $module_endpoints, $target_endpoint); - - $code = $output['code']; - http_response_code($code); - echo $output['body']; - - } - - function batch() { - - $module_name = segment(3); - $this->_make_sure_table_exists($module_name); - $module_endpoints = $this->_fetch_endpoints($module_name); - - $endpoint_name = 'Insert Batch'; - $token_validation_data['endpoint'] = $endpoint_name; - $token_validation_data['module_name'] = $module_name; - $token_validation_data['module_endpoints'] = $module_endpoints; - $input['token'] = $this->_validate_token($token_validation_data); - - $output['token'] = $input['token']; - - $post = file_get_contents('php://input'); - $decoded = json_decode($post, true); - $data = []; - - if (count($decoded)>0) { - - foreach ($decoded as $key => $value) { - $row_data = $this->_get_params_from_post($value); - $data[] = $row_data; - } - - } - - $input['params'] = $data; - $input['module_name'] = $module_name; - $input['endpoint'] = 'Insert Batch'; - - $module_endpoints = $this->_fetch_endpoints($input['module_name']); - $target_endpoint = $module_endpoints[$input['endpoint']]; - $input = $this->_attempt_invoke_before_hook($input, $module_endpoints, $target_endpoint); - extract($input); - - if (count($data)>0) { - //execute batch insert and return num rows inserted - $row_count = $this->model->insert_batch($module_name, $data); - $output['body'] = $row_count; - $output['code'] = 200; - } else { - $output['code'] = 422; - $output['body'] = 'No rows were inserted.'; - } - - //attempt invoke 'after' hook - $output['module_name'] = $module_name; - $output = $this->_attempt_invoke_after_hook($output, $module_endpoints, $target_endpoint); - - $code = $output['code']; - http_response_code($code); - echo $output['body']; - } - - function update() { - - $module_name = segment(3); - $this->_make_sure_table_exists($module_name); - $module_endpoints = $this->_fetch_endpoints($module_name); - - $endpoint_name = 'Update'; - $token_validation_data['endpoint'] = $endpoint_name; - $token_validation_data['module_name'] = $module_name; - $token_validation_data['module_endpoints'] = $module_endpoints; - $input['token'] = $this->_validate_token($token_validation_data); - - $output['token'] = $input['token']; - - $update_id = segment(4); - - if (!is_numeric($update_id)) { - http_response_code(400); - echo 'Non numeric update id.'; - die(); - } else { - $post = file_get_contents('php://input'); - $decoded = json_decode($post, true); - } - - if (count($decoded)>0) { - $params = $this->_get_params_from_post($decoded); - } else { - $params = []; - } - - //attempt invoke 'before' hook - $input['params'] = $params; - $input['module_name'] = $module_name; - $input['endpoint'] = $endpoint_name; - - $module_endpoints = $this->_fetch_endpoints($input['module_name']); - $target_endpoint = $module_endpoints[$input['endpoint']]; - $input = $this->_attempt_invoke_before_hook($input, $module_endpoints, $target_endpoint); - extract($input); - - if (isset($params['id'])) { - unset($params['id']); //id cannot be changed - } - - if (count($params)>0) { - //make sure params are valid - $this->_make_sure_columns_exist($module_name, $params); - $this->model->update($update_id, $params, $module_name); - $result = $this->model->get_where($update_id, $module_name); - - if ($result == false) { - $output['code'] = 422; - } else { - $output['code'] = 200; - } - - $output['body'] = json_encode($result); - } else { - $output['code'] = 422; - $output['body'] = ''; - } - - $output['module_name'] = $module_name; - $output = $this->_attempt_invoke_after_hook($output, $module_endpoints, $target_endpoint); - - $code = $output['code']; - http_response_code($code); - echo $output['body']; - } - - function delete() { - - $module_name = segment(3); - $this->_make_sure_table_exists($module_name); - $module_endpoints = $this->_fetch_endpoints($module_name); - - $endpoint_name = 'Delete One'; - $token_validation_data['endpoint'] = $endpoint_name; - $token_validation_data['module_name'] = $module_name; - $token_validation_data['module_endpoints'] = $module_endpoints; - $input['token'] = $this->_validate_token($token_validation_data); - - $output['token'] = $input['token']; - - $id = segment(4); - - //attempt invoke 'before' hook - $data['id'] = $id; - $input['params'] = $data; - $input['module_name'] = $module_name; - $input['endpoint'] = $endpoint_name; - - $module_endpoints = $this->_fetch_endpoints($input['module_name']); - $target_endpoint = $module_endpoints[$input['endpoint']]; - $input = $this->_attempt_invoke_before_hook($input, $module_endpoints, $target_endpoint); - extract($input); - $update_id = $input['params']['id']; - - if (!is_numeric($update_id)) { - http_response_code(400); - echo 'Non numeric id.'; + if (strtolower(ENV) !== 'dev') { + http_response_code(403); //forbidden + echo "API Explorer disabled since not in 'dev' mode."; die(); } - $result = $this->model->get_where($update_id, $module_name); - - if ($result == false) { - http_response_code(422); - echo 'false'; - die(); - } else { - - $this->model->delete($update_id, $module_name); - $output['body'] = 'true'; - $output['code'] = 200; - $output['module_name'] = $module_name; - $output = $this->_attempt_invoke_after_hook($output, $module_endpoints, $target_endpoint); - - $code = $output['code']; - http_response_code($code); - echo $output['body']; - - } - - } - - function destroy() { - - $module_name = segment(3); - $this->_make_sure_table_exists($module_name); - $module_endpoints = $this->_fetch_endpoints($module_name); - - $endpoint_name = 'Destroy'; - $token_validation_data['endpoint'] = $endpoint_name; - $token_validation_data['module_name'] = $module_name; - $token_validation_data['module_endpoints'] = $module_endpoints; - $input['token'] = $this->_validate_token($token_validation_data); - $output['token'] = $input['token']; - - //get posted params (PHP doesn't differentiate btn GET and DELETE) - $post = file_get_contents('php://input'); - $decoded = json_decode($post, true); - - if (count($decoded)>0) { - $params = $this->_get_params_from_post($decoded); - } else { - $params = []; - } - - $sql = 'select * from '.$module_name; - - //attempt invoke 'before' hook - $input['params'] = $params; - $input['module_name'] = $module_name; - $input['endpoint'] = 'Destroy'; - - $module_endpoints = $this->_fetch_endpoints($input['module_name']); - $target_endpoint = $module_endpoints[$input['endpoint']]; - $input = $this->_attempt_invoke_before_hook($input, $module_endpoints, $target_endpoint); - extract($input); + $target_table = segment(3); + $this->make_sure_table_exists($target_table); - $num_params = count($params); + $golden_token = $this->generate_new_golden_token(); - if ($num_params < 1) { - $rows = $this->model->get('id', $module_name); - $num_rows_affected = count($rows); + //fetch an array of all endpoints that exist for this table + $endpoints = $this->fetch_endpoints($target_table); - } else { - //params were posted - $sql = 'select * from '.$module_name; - $params = json_encode($params); - $params = ltrim($params); - $params = json_decode($params); - $params = get_object_vars($params); - $query_info = $this->_add_params_to_query($module_name, $sql, $params); - - $sql = $query_info['sql']; - $data = $query_info['data']; - - if (count($data)<1) { - $rows = $this->model->query($sql, 'object'); - } else { - $rows = $this->model->query_bind($sql, $data, 'object'); - } - - $num_rows_affected = count($rows); - - } - - $msg = $num_rows_affected; - - if ($num_rows_affected>0) { - - $sql = substr($sql, 13, strlen($sql)); - $sql = 'delete from'.$sql; - - if (!isset($data)) { - $this->model->query($sql); - } else { - $this->model->query_bind($sql, $data); - } - } - - $output['body'] = $msg; - $output['code'] = 200; - $output['module_name'] = $module_name; - - $output = $this->_attempt_invoke_after_hook($output, $module_endpoints, $target_endpoint); - - $code = $output['code']; - http_response_code($code); - echo $output['body']; - } - - function _api_manager_error($response_status_code, $error_msg) { - http_response_code($response_status_code); - echo $error_msg; - die(); - } - - function _not_allowed_msg() { - http_response_code(401); - echo "Invalid token."; die(); - } - - function _fetch_endpoint_auth_rules($endpoint) { - - if (!isset($endpoint['authorization'])) { - $endpoint['authorization'] = false; - } - - return $endpoint['authorization']; + //this is the location for the api.json file that defines the API endpoints + $endpoint_settings_location = '/modules/'.$target_table.'/assets/api.json'; + $http_status_codes = $this->get_status_codes(); + $columns = $this->get_table_columns($target_table); + $view_file = $file_path = APPPATH.'engine/views/api_explorer.php'; + require_once $view_file; } - function _make_sure_table_exists($table) { - $all_tables = $this->_get_all_tables(); + private function make_sure_table_exists($table) { + $all_tables = $this->get_all_tables(); if(!in_array($table, $all_tables)) { http_response_code(422); echo 'invalid table name'; die(); } } - function _make_sure_columns_exist($module_name, $params) { - //make sure columns exists on the table - $invalid_columns = []; - $columns = $this->_get_all_columns($module_name); - - foreach ($params as $key => $value) { - if (!in_array($key,$columns)) { - $invalid_columns[] = $key; - } - } - - if (count($invalid_columns)) { - $error_count = 0; - http_response_code(422); - - $msg = "The following fields are not valid; "; - - if (count($invalid_columns)==1) { - $msg = str_replace('fields are', 'field is', $msg); - } - - echo $msg; - - foreach ($invalid_columns as $invalid_field) { - $error_count++; - echo $invalid_field; - - if ($error_countmodule('trongate_tokens'); - $target_module = segment(3); - $this->_make_sure_table_exists($target_module); - $this->module('trongate_tokens'); - - $token_data['user_id'] = $this->trongate_tokens->_get_user_id(); - $token_data['code'] = 'aaa'; - - $sql = 'delete from trongate_tokens where user_id = :user_id and code = :code'; - $this->model->query_bind($sql, $token_data); - - $token_data['expiry_date'] = time() + 7200; //two hours - $data['golden_token'] = $this->trongate_tokens->_generate_token($token_data); - $data['endpoints'] = $this->_fetch_endpoints($target_module); - $data['endpoint_settings_location'] = '/modules/'.$target_module.'/assets/api.json'; - $columns = $this->_get_all_columns($target_module); - - //build columns as json_str - $json_starter_str = '{'; - $count = 1; - foreach ($columns as $column) { - $count++; - if ($column !== 'id') { - - if ($count == 3) { - $column_length = strlen($column); - $cursor_reset_position = $column_length + 10; - } - - $json_starter_str.= '"'.$column.'":""'; - - if ($count <= count($columns)) { - $json_starter_str.= ','; - } else { - $json_starter_str.= '}'; - } - - } - } - - $new_line = '\n'; - $indent = ' '; - $json_starter_str = str_replace('{', '{'.$new_line.$indent, $json_starter_str); - $json_starter_str = str_replace(',', ','.$new_line.$indent, $json_starter_str); - $json_starter_str = trim(str_replace('}', $new_line.'}'.$new_line.$indent, $json_starter_str)); - - $data['cursor_reset_position'] = $cursor_reset_position; - $data['json_starter_str'] = $json_starter_str; - $view_file = $file_path = APPPATH.'engine/views/api_explorer.php'; - - extract($data); - require_once $view_file; - } - - function _get_all_tables() { + private function get_all_tables() { $tables = []; $sql = 'show tables'; $column_name = 'Tables_in_'.DATABASE; @@ -1056,385 +48,168 @@ function _get_all_tables() { return $tables; } - function _fetch_endpoints($target_module) { + private function generate_new_golden_token() { + $token_data['user_id'] = 0; + $token_data['code'] = 'aaa'; //for bypassing auth rules - if ($target_module == '') { + //get rid of any existing tokens before generating a new (golden) token + $sql = 'delete from trongate_tokens where user_id = :user_id and code = :code'; + $this->model->query_bind($sql, $token_data); + + $this->module('trongate_tokens'); + $token_data['expiry_date'] = time() + 7200; //two hours + $golden_token = $this->trongate_tokens->_generate_token($token_data); + return $golden_token; + } + + private function fetch_endpoints($target_table) { + if ($target_table === '') { http_response_code(422); - echo "No target module set"; die(); + echo "No target table set"; die(); } - $file_path = APPPATH.'modules/'.$target_module.'/assets/api.json'; + $file_path = APPPATH.'modules/'.$target_table.'/assets/api.json'; $settings = file_get_contents($file_path); $endpoints = json_decode($settings, true); return $endpoints; } - function _get_all_columns($table) { - - $columns = []; + private function get_status_codes() { + $http_status_codes = array( + "CODE_200" => "OK", + "CODE_201" => "Created", + "CODE_202" => "Accepted", + "CODE_203" => "Non-Authoritative Information", + "CODE_204" => "No Content", + "CODE_205" => "Reset Content", + "CODE_206" => "Partial Content", + "CODE_300" => "Multiple Choices", + "CODE_301" => "Moved Permanently", + "CODE_302" => "Found", + "CODE_303" => "See Other", + "CODE_304" => "Not Modified", + "CODE_305" => "Use Proxy", + "CODE_307" => "Temporary Redirect", + "CODE_400" => "Bad Request", + "CODE_401" => "Unauthorized", + "CODE_402" => "Payment Required", + "CODE_403" => "Forbidden", + "CODE_404" => "Not Found", + "CODE_405" => "Method Not Allowed", + "CODE_406" => "Not Acceptable", + "CODE_407" => "Proxy Authentication Required", + "CODE_408" => "Request Timeout", + "CODE_409" => "Conflict", + "CODE_410" => "Gone", + "CODE_411" => "Length Required", + "CODE_412" => "Precondition Failed", + "CODE_413" => "Request Entity Too Large", + "CODE_414" => "Request-URI Too Long", + "CODE_415" => "Unsupported Media Type", + "CODE_416" => "Requested Range Not Satisfiable", + "CODE_417" => "Expectation Failed", + "CODE_422" => "Unprocessable Entity", + "CODE_500" => "Internal Server Error", + "CODE_501" => "Not Implemented", + "CODE_502" => "Bad Gateway", + "CODE_503" => "Service Unavailable", + "CODE_504" => "Gateway Timeout", + "CODE_505" => "HTTP Version Not Supported" + ); + + return $http_status_codes; + } + + private function get_table_columns($table, $simplify_output=null) { $sql = 'describe '.$table; $rows = $this->model->query($sql, 'array'); - foreach ($rows as $row) { - $columns[] = $row['Field']; - } - - return $columns; - } - - function _get_params_from_url($params_segment) { - //params segment is where params might be passed - $params_str = segment($params_segment); - $first_char = substr($params_str, 0, 1); - if ($first_char == '?') { - $params_str = substr($params_str, 1); - } - - $data = []; - parse_str($params_str, $data); - - //convert into json - $params = []; - foreach ($data as $key => $value) { - $key = $this->_prep_key($key); - $value = $this->_remove_special_characters($value); - $params[$key] = $value; - } - return $params; - } - - function _get_params_from_post($decoded) { - - $params = []; - foreach ($decoded as $key => $value) { - $key = $this->_prep_key($key); - $value = $this->_remove_special_characters($value); - $params[$key] = $value; - } - - return $params; + if(isset($simplify_output)) { + $columns = []; + foreach($rows as $row) { + $columns[] = $row['Field']; + } + return $columns; + } else { + return $rows; + } } - function _attempt_invoke_before_hook($input, $module_endpoints, $target_endpoint) { + public function get() { //GET + $request_type = $_SERVER['REQUEST_METHOD']; - //check API settings & find out which method (if any) to invoke - if (isset($target_endpoint['beforeHook'])) { - //invoke the before hook - $module_name = $input['module_name']; - $target_method = $target_endpoint['beforeHook']; - $input = Modules::run($module_name.'/'.$target_method, $input); + if($request_type !== 'GET') { + $this->search(); + return; } - return $input; - } - - function _attempt_invoke_after_hook($output, $module_endpoints, $target_endpoint) { - //check API settings & find out which method (if any) to invoke - - if (isset($target_endpoint['afterHook'])) { - //invoke the after hook - $module_name = $output['module_name']; - $code = $output['code']; - $target_method = $target_endpoint['afterHook']; - - $output = $this->_clean_output($output); - - //ONLY output['body'] and output['code'] is being used at this point - $output = Modules::run($module_name.'/'.$target_method, $output); + require_once('Standard_endpoints.php'); + $se = new Standard_endpoints(); + if(is_numeric(segment(4))) { + $se->find_one(); } else { - $output = $this->_clean_output($output); + $se->get(); } - - return $output; + } - function _clean_output($output) { - //remove unwanted output properties - foreach ($output as $key => $value) { - - if (($key != 'body') && ($key != 'code') && ($key != 'token')) { - unset($output[$key]); - } - } - - return $output; + public function search() { //POST + require_once('Standard_endpoints.php'); + $se = new Standard_endpoints(); + $se->search(); } - function _prep_key($key) { //php convert json into URL string - - //get last char - $key = trim($key); - $str_len = strlen($key); - $last_char = substr($key, $str_len-1); - - if ($last_char == '!') { - $key = $key.'='; - } - - $key = $this->_remove_special_characters($key); - - $ditch = 'OR_'; - $replace = 'OR '; - $key = str_replace($ditch, $replace, $key); - - $ditch = '_NOT_LIKE'; - $replace = ' NOT LIKE'; - $key = str_replace($ditch, $replace, $key); - - $ditch = '_LIKE'; - $replace = ' LIKE'; - $key = str_replace($ditch, $replace, $key); - - $ditch = '_!='; - $replace = ' !='; - $key = str_replace($ditch, $replace, $key); - - $ditch = 'AND_'; - $replace = 'AND '; - $key = str_replace($ditch, $replace, $key); - - $ditch = '_>'; - $replace = ' >'; - $key = str_replace($ditch, $replace, $key); - - $ditch = '_<'; - $replace = ' <'; - $key = str_replace($ditch, $replace, $key); - - return $key; + public function exists() { //GET + require_once('Standard_endpoints.php'); + $se = new Standard_endpoints(); + $se->exists(); } - function _remove_special_characters($str) { - $ditch = '*!underscore!*'; - $replace = '_'; - $str = str_replace($ditch, $replace, $str); - - $ditch = '*!gt!*'; - $replace = '>'; - $str = str_replace($ditch, $replace, $str); - - $ditch = '*!lt!*'; - $replace = '<'; - $str = str_replace($ditch, $replace, $str); - - $ditch = '*!equalto!*'; - $replace = '='; - $str = str_replace($ditch, $replace, $str); - - return $str; + public function count() { //GET + $request_type = $_SERVER['REQUEST_METHOD']; + require_once('Standard_endpoints.php'); + $se = new Standard_endpoints(); + $se->count(); } - function _add_params_to_query($module_name, $sql, $params) { - - //variables have been posted - start from here - $got_where = false; - foreach ($params as $key => $value) { - $param_type = $this->_get_param_type($module_name, $key); - - if ($param_type == 'where') { - $where_conditions[$key] = $value; - } - } - - //add where conditions - if (isset($where_conditions)) { - $where_condition_count = 0; - foreach ($where_conditions as $where_left_side => $where_value) { - $where_condition_count++; - //where_key where_value - //manipulate the SQL query - - $where_key = $this->_extract_where_key($where_left_side); - - //make sure this column exists on the table - $columns = $this->_get_all_columns($module_name); - if (!in_array($where_key, $columns)) { - http_response_code(422); - echo $where_key.' is not a valid value or column name.'; - die(); - } - - $where_start_word = $this->_extract_where_start_word($where_left_side, $where_condition_count); - $connective = $this->_extract_connective($where_left_side); - - $new_where_condition = $where_start_word.' '.$where_key.' '.$connective.' :'.$where_key; - $sql = $sql.' '.$new_where_condition; - $data[$where_key] = $where_value; - - } - - } - - //add order by - if (isset($params['orderBy'])) { - - //make sure this column is on the table - $columns = $this->_get_all_columns($module_name); - $column_name = str_replace(' desc', '', $params['orderBy']); - - if (!in_array($column_name, $columns)) { - //invalid order by - http_response_code(422); - echo "invalid order by"; die(); - } - - $sql = $sql.' order by '.$params['orderBy']; - unset($params['orderBy']); - } - - //add limit offset - if (isset($params['limit'])) { - - $limit = $params['limit']; - - //get the offset - if (isset($params['offset'])) { - $offset = $params['offset']; - } else { - $offset = 0; - } - - if ((!is_numeric($limit)) || (!is_numeric($offset))) { - http_response_code(422); - echo "non numeric limit and/or offset"; die(); - } - - settype($limit, "integer"); - settype($offset, "integer"); - - $data['limit'] = $limit; - $data['offset'] = $offset; - $sql = $sql.= ' limit :offset, :limit'; - - } - - if (!isset($data)) { - $data = []; - } - - $query_info['sql'] = $sql; - $query_info['data'] = $data; - return $query_info; + public function create() { //POST + require_once('Standard_endpoints.php'); + $se = new Standard_endpoints(); + $se->create(); } - function _get_param_type($module_name, $key) { - - switch ($key) { - case 'limit': - $type = 'limit'; - break; - case 'offset': - $type = 'offset'; - break; - case 'orderBy': - $type = 'order by'; - break; - default: - $type = 'where'; - break; + public function insert() { //POST + $request_type = $_SERVER['REQUEST_METHOD']; + if ($request_type === 'GET') { + http_response_code(400); + die(); } - - - return $type; + echo 'API insert batch'; die(); } - function _extract_where_key($where_left_side) { - - $where_left_side = trim($where_left_side); - $bits = explode(' ', $where_left_side); - - $first_three = substr($where_left_side, 0, 3); - if ($first_three == 'OR ') { - $where_key = $bits[1]; - } else { - $where_key = $bits[0]; - } - - return $where_key; + public function batch() { //POST + require_once('Standard_endpoints.php'); + $se = new Standard_endpoints(); + $se->insert(); } - function _extract_where_start_word($where_left_side, $where_condition_count) { - //return WHERE, AND or OR - $where_start_word = 'WHERE'; - $where_left_side = trim($where_left_side); - - $first_three = substr($where_left_side, 0, 3); - if ($first_three == 'OR ') { - $where_start_word = 'OR'; - } elseif ($where_condition_count>1) { - $where_start_word = 'AND'; - } - - return $where_start_word; + public function update() { //POST or PUT + require_once('Standard_endpoints.php'); + $se = new Standard_endpoints(); + $se->update(); } - function _extract_connective($where_left_side) { - - /* - * = { "name":"John"} - * OR { "OR age >" : 21} - * != { "name !=": "John"} - * > { "age >" : 21} - * < { "age <" : 21} - * LIKE { "name LIKE" : "e"} - * NOT LIKE { "name NOT LIKE" : "e"} - */ - - $where_left_side = trim($where_left_side); - $str_len = strlen($where_left_side); - $start = $str_len - 9; - $last_nine = substr($where_left_side, $start, $str_len); - - if ($last_nine == ' NOT LIKE') { - $connective = 'NOT LIKE'; - } - - $ditch = ' NOT LIKE'; - $replace = ''; - $where_left_side = str_replace($ditch, $replace, $where_left_side); - - if (!isset($connective)) { - $start = $str_len - 5; - $last_five = substr($where_left_side, $start, $str_len); - - if ($last_five == ' LIKE') { - $connective = 'LIKE'; - } - } - - $ditch = ' LIKE'; - $replace = ''; - $where_left_side = str_replace($ditch, $replace, $where_left_side); - - if (!isset($connective)) { - - $first_three = substr($where_left_side, 0, 3); - if ($first_three == 'OR ') { - $where_left_side = substr($where_left_side, 3, $str_len); - } - - $bits = explode(' ', $where_left_side); - $num_bits = count($bits); - - if ($num_bits>1) { - $target_index = count($bits)-1; - $connective = $bits[$target_index]; - } else { - $connective = '='; - } - - } - - $connective = ltrim(trim($connective)); - return $connective; + public function destroy() { //POST or DELETE + require_once('Standard_endpoints.php'); + $se = new Standard_endpoints(); + $se->destroy(); } - function submit_bypass_auth() { - $post = file_get_contents('php://input'); - $decoded = json_decode($post, true); - $this->module('trongate_tokens'); - $this->trongate_tokens->_attempt_generate_bypass_token(); + public function delete() { //POST or DELETE + require_once('Standard_endpoints.php'); + $se = new Standard_endpoints(); + $se->delete_one(); } } \ No newline at end of file diff --git a/engine/Core.php b/engine/Core.php index ec5ca833..b6cf726d 100644 --- a/engine/Core.php +++ b/engine/Core.php @@ -59,7 +59,7 @@ private function serve_module_asset() { if (file_exists($asset_path)) { $content_type = mime_content_type($asset_path); - if ($content_type == 'text/plain' || $content_type == 'text/html') { + if ($content_type === 'text/plain' || $content_type === 'text/html') { $pos2 = strpos($file_name, '.css'); if (is_numeric($pos2)) { @@ -73,12 +73,12 @@ private function serve_module_asset() { } - if ($content_type == 'image/svg') { + if ($content_type === 'image/svg') { $content_type.= '+xml'; } //make sure not a PHP file or api.json - if((is_numeric(strpos($content_type, 'php'))) || ($file_name == 'api.json')) { + if((is_numeric(strpos($content_type, 'php'))) || ($file_name === 'api.json')) { http_response_code(422); die(); } @@ -119,7 +119,7 @@ private function serve_child_module_asset($asset_path, $file_name) { $content_type = mime_content_type($asset_path); - if ($content_type == 'text/plain'|| $content_type == 'text/html') { + if ($content_type === 'text/plain'|| $content_type === 'text/html') { $pos2 = strpos($file_name, '.css'); if (is_numeric($pos2)) { $content_type = 'text/css'; @@ -174,7 +174,7 @@ private function serve_controller() { $this->current_method = strtolower($method_with_no_params); //make sure not private $str = substr($this->current_method, 0, 1); - if ($str == '_') { + if ($str === '_') { $this->draw_error_page(); } } @@ -187,8 +187,18 @@ private function serve_controller() { $controller_path = '../modules/'.$this->current_module.'/controllers/'.$this->current_controller.'.php'; - if ($controller_path == '../modules/api/controllers/Api.php') { + if ($controller_path === '../modules/api/controllers/Api.php') { + //API intercept, since segment(1) is 'api/' $controller_path = '../engine/Api.php'; + require_once $controller_path; + $target_method = $this->current_method; + $this->current_controller = new $this->current_controller($this->current_module); + if (method_exists($this->current_controller, $this->current_method)) { + $this->current_controller->$target_method($this->current_value); + return; + } else { + $this->draw_error_page(); + } } if (!file_exists($controller_path)) { @@ -197,7 +207,7 @@ private function serve_controller() { require_once $controller_path; - if (ENV == 'dev') { + if (strtolower(ENV) == 'dev') { $this->attempt_sql_transfer($controller_path); } @@ -206,6 +216,24 @@ private function serve_controller() { $this->current_controller = new $this->current_controller($this->current_module); $this->current_controller->$target_method($this->current_value); } else { + //method does not exist - attempt invoke standard endpoint + $this->current_controller = 'Standard_endpoints'; + $controller_path = '../engine/Standard_endpoints.php'; + require_once $controller_path; + $se = new Standard_endpoints(); + $endpoint_index = $se->attempt_find_endpoint_index(); + + if ($endpoint_index !== '') { + $target_method = $this->current_method; + if(is_numeric($target_method)) { + $se->attempt_serve_standard_endpoint($endpoint_index); + } else { + $se->$target_method($this->current_value); + } + + return; + } + $this->draw_error_page(); } } diff --git a/engine/Standard_endpoints.php b/engine/Standard_endpoints.php new file mode 100644 index 00000000..3739a5e1 --- /dev/null +++ b/engine/Standard_endpoints.php @@ -0,0 +1,1103 @@ + "%21=", + "<=" => "%3C=", + ">=" => "%3E=", + "=" => "=", + "<" => "%3C", + ">" => "%3E" + ]; + + function __construct() { + parent::__construct(); + } + + private function get_request_type() { + header('Access-Control-Allow-Headers: X-HTTP-Method-Override'); + $request_type = $_SERVER['REQUEST_METHOD']; + + // Check for X-HTTP-Method-Override header + if (($request_type !== 'GET') && (isset($_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE']))) { + // Get the desired HTTP method from the X-HTTP-Method-Override header + $request_type = strtoupper($_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE']); + } + + return $request_type; + } + + public function index() { + $request_type = $this->get_request_type(); + switch ($request_type) { + case 'GET': + $this->get(); + break; + case 'POST': + $this->create(); + break; + case 'DELETE': + $this->destroy(); + break; + default: + $this->get(); + break; + } + } + + public function get($return_row_count=false) { //GET + //get the endpoint from the api.json file (and make sure allowed!) + $endpoint_name = $return_row_count === true ? 'Count' : 'Get'; + $table_name = segment(1) === 'api' ? segment(3) : segment(1); + $table_name = remove_query_string($table_name); + $input = $this->get_target_endpoint($table_name, $endpoint_name); + + $before_hook = $input['target_endpoint']['beforeHook'] ?? ''; + $after_hook = $input['target_endpoint']['afterHook'] ?? ''; + unset($input['target_endpoint']); + + if ($before_hook !== '') { + $input = $this->attempt_invoke_before_hook($table_name, $before_hook, $input); + } + + $standard_query = 'SELECT * FROM '.$table_name; + $rows = $this->fetch_rows($standard_query, $input['params']); + + $output['token'] = $input['token']; + $output['body'] = $return_row_count === true ? count($rows) : json_encode($rows); + $output['code'] = 200; + + if ($after_hook !== '') { + $output = $this->attempt_invoke_after_hook($table_name, $after_hook, $output); + } + + $output = $this->clean_output($output); + http_response_code($output['code']); + echo $output['body']; + die(); + } + + public function search($return_row_count=false) { //POST + $request_type = $this->get_request_type(); + if($request_type !== 'POST') { + http_response_code(400); + die(); + } + + $table_name = segment(1) === 'api' ? segment(3) : segment(1); + $table_name = remove_query_string($table_name); + $endpoint_name = $return_row_count === true ? 'Count By Post' : 'Search'; + + $input = $this->get_target_endpoint($table_name, $endpoint_name); + $before_hook = $input['target_endpoint']['beforeHook'] ?? ''; + $after_hook = $input['target_endpoint']['afterHook'] ?? ''; + unset($input['target_endpoint']); + + if ($before_hook !== '') { + $input = $this->attempt_invoke_before_hook($table_name, $before_hook, $input); + } + + $standard_query = 'SELECT * FROM '.$table_name; + $rows = $this->fetch_rows($standard_query, $input['params']); + + $output['token'] = $input['token']; + $output['body'] = $return_row_count === true ? count($rows) : json_encode($rows); + $output['code'] = 200; + + if ($after_hook !== '') { + $output = $this->attempt_invoke_after_hook($table_name, $after_hook, $output); + } + + $output = $this->clean_output($output); + http_response_code($output['code']); + echo $output['body']; + die(); + } + + public function find_one() { //GET + $request_type = $this->get_request_type(); + if ($request_type !== 'GET') { + http_response_code(400); + die(); + } + + $target_segment = segment(1) === 'api' ? 4 : 2; + $update_id = intval(segment($target_segment)); + + if ($update_id > 0) { + $table_name = segment(1) === 'api' ? segment(3) : segment(1); + $table_name = remove_query_string($table_name); + $input = $this->get_target_endpoint($table_name, 'Find One'); + $before_hook = $input['target_endpoint']['beforeHook'] ?? ''; + $after_hook = $input['target_endpoint']['afterHook'] ?? ''; + unset($input['target_endpoint']); + + if ($before_hook !== '') { + $input = $this->attempt_invoke_before_hook($table_name, $before_hook, $input); + } + + $result = $this->model->get_where($update_id, $table_name); + if ($result === false) { + $this->api_manager_error(404, 'Record not found'); + } + + $output['token'] = $input['token']; + $output['body'] = json_encode($result); + $output['code'] = 200; + + if ($after_hook !== '') { + $output = $this->attempt_invoke_after_hook($table_name, $after_hook, $output); + } + + $output = $this->clean_output($output); + http_response_code($output['code']); + echo $output['body']; + die(); + } else { + $this->api_manager_error(400, 'Non-numeric update ID!'); + } + + } + + public function exists() { //GET + $request_type = $this->get_request_type(); + if ($request_type !== 'GET') { + http_response_code(400); + die(); + } + + $target_segment = segment(1) === 'api' ? 4 : 2; + $update_id = intval(segment($target_segment)); + $table_name = segment(1) === 'api' ? segment(3) : segment(1); + $table_name = remove_query_string($table_name); + + //get the endpoint from the api.json file (and make sure allowed!) + $input = $this->get_target_endpoint($table_name, 'Exists'); + $before_hook = $input['target_endpoint']['beforeHook'] ?? ''; + $after_hook = $input['target_endpoint']['afterHook'] ?? ''; + unset($input['target_endpoint']); + + if ($before_hook !== '') { + $input = $this->attempt_invoke_before_hook($table_name, $before_hook, $input); + } + + $result = $this->model->get_where($update_id, $table_name); + + $output['token'] = $input['token']; + $output['body'] = $result === false ? 'false' : 'true'; + $output['code'] = 200; + + if ($after_hook !== '') { + $output = $this->attempt_invoke_after_hook($table_name, $after_hook, $output); + } + + $output = $this->clean_output($output); + http_response_code($output['code']); + echo $output['body']; + die(); + + } + + public function count() { //GET or POST + $request_type = $this->get_request_type(); + if ($request_type !== 'GET') { + $this->search(true); + } else { + $this->get(true); + } + } + + public function create() { + $request_type = $this->get_request_type(); + if ($request_type !== 'POST') { + http_response_code(400); + die(); + } + $table_name = segment(1) === 'api' ? segment(3) : segment(1); + $table_name = remove_query_string($table_name); + + $input = $this->get_target_endpoint($table_name, 'Create'); + $before_hook = $input['target_endpoint']['beforeHook'] ?? ''; + $after_hook = $input['target_endpoint']['afterHook'] ?? ''; + unset($input['target_endpoint']); + + //make sure params have been posted + if(count($input['params']) == 0) { + $this->api_manager_error(400, 'No posted data!'); + } + + if ($before_hook !== '') { + $input = $this->attempt_invoke_before_hook($table_name, $before_hook, $input); + } + + $columns_exist_result = $this->make_sure_columns_exist($table_name, $input['params']); + + if($columns_exist_result !== true) { + $this->api_manager_error(400, $columns_exist_result); + } + + //attempt insert new record + try { + $new_id = $this->model->insert($input['params'], $table_name); + $new_record_obj = $this->model->get_where($new_id, $table_name); + + $output['token'] = $input['token']; + $output['code'] = 200; + $output['body'] = json_encode($new_record_obj); + + if ($after_hook !== '') { + $output = $this->attempt_invoke_after_hook($table_name, $after_hook, $output); + } + + $output = $this->clean_output($output); + http_response_code($output['code']); + echo $output['body']; + die(); + + } catch (Exception $e) { + $this->api_manager_error(400, $e); + } + + } + + function make_sure_columns_exist($table_name, $params, $valid_columns=NULL) { + + if(!isset($valid_columns)) { + $valid_columns = $this->get_all_columns($table_name); + } + + $invalid_columns = []; + foreach ($params as $column => $value) { + if (!in_array($column, $valid_columns)) { + $invalid_columns[] = $column; + } + } + if (count($invalid_columns) === 0) { + return true; + } elseif (count($invalid_columns) === 1) { + $message = "The {$invalid_columns[0]} column does not exist on the {$table_name} table!"; + return $message; + } else { + $message = "The following columns do not exist on the {$table_name} table: " . implode(', ', $invalid_columns); + return $message; + } + } + + private function get_all_columns($table) { + $columns = []; + $sql = 'describe '.$table; + $rows = $this->model->query($sql, 'array'); + foreach ($rows as $row) { + $columns[] = $row['Field']; + } + + return $columns; + } + + public function insert() { // BATCH INSERT! + + $request_type = $this->get_request_type(); + if ($request_type !== 'POST') { + http_response_code(400); + die(); + } + $table_name = segment(1) === 'api' ? segment(3) : segment(1); + $table_name = remove_query_string($table_name); + $input = $this->get_target_endpoint($table_name, 'Insert Batch'); + + $before_hook = $input['target_endpoint']['beforeHook'] ?? ''; + $after_hook = $input['target_endpoint']['afterHook'] ?? ''; + unset($input['target_endpoint']); + + //fetch the posted data + $post = trim(file_get_contents('php://input')); + + if($post === '') { + $this->api_manager_error(400, 'No posted data!'); + } + + $posted_items = json_decode($post, true); + if (!is_array($posted_items) || substr($post, 0, 1) !== '[' || substr($post, -1) !== ']') { + $this->api_manager_error(400, 'Invalid format: Not an array of objects!'); + } + + if(count($posted_items) === 0) { + $this->api_manager_error('400', 'No posted data!'); + } + + $input['params'] = $posted_items; + + if ($before_hook !== '') { + $input = $this->attempt_invoke_before_hook($table_name, $before_hook, $input); + } + + //loop through the posted data and make sure all of the columns exist on the table + $valid_columns = $this->get_all_columns($table_name); + foreach($input['params'] as $posted_item) { + $columns_exist_result = $this->make_sure_columns_exist($table_name, $posted_item); + + if($columns_exist_result !== true) { + $this->api_manager_error(400, $columns_exist_result); + } + + } + + //attempt batch insert records + try { + + $row_count = $this->model->insert_batch($table_name, $input['params']); + $output['body'] = $row_count; + $output['code'] = 200; + + if ($after_hook !== '') { + $output = $this->attempt_invoke_after_hook($table_name, $after_hook, $output); + } + + $output = $this->clean_output($output); + http_response_code($output['code']); + echo $output['body']; + die(); + + } catch (Exception $e) { + $this->api_manager_error(400, $e); + } + + } + + public function update() { //POST or PUT + $allowed_request_types = (segment(1) === 'api') ? array('POST', 'PUT') : array('PUT'); + $target_segment = (segment(1) === 'api') ? 4 : 2; + $update_id = intval(segment($target_segment)); + + $request_type = $this->get_request_type(); + + if (!in_array($request_type, $allowed_request_types)) { + http_response_code(400); + die(); + } + + $table_name = segment(1) === 'api' ? segment(3) : segment(1); + $table_name = remove_query_string($table_name); + + $input = $this->get_target_endpoint($table_name, 'Update'); + $before_hook = $input['target_endpoint']['beforeHook'] ?? ''; + $after_hook = $input['target_endpoint']['afterHook'] ?? ''; + unset($input['target_endpoint']); + + //make sure params have been posted + if(count($input['params']) == 0) { + $this->api_manager_error(400, 'No posted data!'); + } + + if ($before_hook !== '') { + $input = $this->attempt_invoke_before_hook($table_name, $before_hook, $input); + } + + $columns_exist_result = $this->make_sure_columns_exist($table_name, $input['params']); + + if($columns_exist_result !== true) { + $this->api_manager_error(400, $columns_exist_result); + } + + //attempt update record + try { + unset($input['params']['id']); + $this->model->update($update_id, $input['params'], $table_name); + $record_obj = $this->model->get_where($update_id, $table_name); + + $output['token'] = $input['token']; + $output['code'] = 200; + $output['body'] = json_encode($record_obj); + + if ($after_hook !== '') { + $output = $this->attempt_invoke_after_hook($table_name, $after_hook, $output); + } + + $output = $this->clean_output($output); + http_response_code($output['code']); + echo $output['body']; + die(); + + } catch (Exception $e) { + $this->api_manager_error(400, $e); + } + + } + + public function destroy() { //POST or DELETE + $allowed_request_types = (segment(1) === 'api') ? array('POST', 'DELETE') : array('DELETE'); + $request_type = $this->get_request_type(); + + if (!in_array($request_type, $allowed_request_types)) { + http_response_code(400); + die(); + } + + $table_name = segment(1) === 'api' ? segment(3) : segment(1); + $table_name = remove_query_string($table_name); + + $input = $this->get_target_endpoint($table_name, 'Destroy'); + $before_hook = $input['target_endpoint']['beforeHook'] ?? ''; + $after_hook = $input['target_endpoint']['afterHook'] ?? ''; + unset($input['target_endpoint']); + + if ($before_hook !== '') { + $input = $this->attempt_invoke_before_hook($table_name, $before_hook, $input); + } + + $standard_query = 'SELECT * FROM '.$table_name; + $rows = $this->fetch_rows($standard_query, $input['params']); + + // Build an array of items to go, based on 'id' + $update_ids = []; + foreach($rows as $row) { + $update_ids[] = $row->id; + } + + try { + + // Check that all the array values are integers + foreach ($update_ids as $update_id) { + if (!is_int($update_id)) { + $this->api_manager_error(400, 'Invalid input: array contains non-integer value!'); + } + } + + // Convert the array of update IDs to a comma-separated string + $update_ids_str = implode(',', $update_ids); + + if(count($update_ids)>0) { + // Build the SQL query to delete rows from the 'tasks' table where 'id' values match those in the array + $sql = "DELETE FROM tasks WHERE id IN ({$update_ids_str})"; + $this->model->query($sql); + } + + $output['token'] = $input['token']; + $output['code'] = 200; + $output['body'] = count($update_ids); + + if ($after_hook !== '') { + $output = $this->attempt_invoke_after_hook($table_name, $after_hook, $output); + } + + $output = $this->clean_output($output); + http_response_code($output['code']); + echo $output['body']; + die(); + + } catch (Exception $e) { + $this->api_manager_error(400, $e); + } + + } + + public function delete_one() { //DELETE + $allowed_request_types = (segment(1) === 'api') ? array('POST', 'DELETE') : array('DELETE'); + $target_segment = (segment(1) === 'api') ? 4 : 2; + $update_id = intval(segment($target_segment)); + + if($update_id <= 0) { + $this->api_manager_error(400, 'Invalid update ID!'); + } + + $request_type = $this->get_request_type(); + + if (!in_array($request_type, $allowed_request_types)) { + http_response_code(400); + die(); + } + + $table_name = segment(1) === 'api' ? segment(3) : segment(1); + $table_name = remove_query_string($table_name); + + $input = $this->get_target_endpoint($table_name, 'Delete One'); + $before_hook = $input['target_endpoint']['beforeHook'] ?? ''; + $after_hook = $input['target_endpoint']['afterHook'] ?? ''; + unset($input['target_endpoint']); + + if ($before_hook !== '') { + $input = $this->attempt_invoke_before_hook($table_name, $before_hook, $input); + } + + $record_obj = $this->model->get_where($update_id, $table_name); + + if($record_obj === false) { + http_response_code(422); + echo 'false'; + die(); + } + + //attempt delete record + try { + $this->model->delete($update_id, $table_name); + + $output['token'] = $input['token']; + $output['code'] = 200; + $output['body'] = 'true'; + + if ($after_hook !== '') { + $output = $this->attempt_invoke_after_hook($table_name, $after_hook, $output); + } + + $output = $this->clean_output($output); + http_response_code($output['code']); + echo $output['body']; + die(); + + } catch (Exception $e) { + $this->api_manager_error(400, $e); + } + + } + + private function attempt_invoke_before_hook($table_name, $before_hook, $input) { + $input = Modules::run($table_name.'/'.$before_hook, $input); + return $input; + } + + private function attempt_invoke_after_hook($table_name, $after_hook, $output) { + $output = Modules::run($table_name.'/'.$after_hook, $output); + return $output; + } + + private function fetch_rows($standard_query, $submitted_params) { + $where_data = $this->extract_where_data($submitted_params); + $order_by_clause = $this->extract_order_by_clause($submitted_params); + $limit_offset_clause = $this->extract_limit_offset_clause($submitted_params); + + $sql = $standard_query; + $where_clause = $where_data['where_clause']; + $params = $where_data['params']; + + if($where_clause !== '') { + $sql.= ' '.$where_clause; + } + + if($order_by_clause !== '') { + $sql.= ' '.$order_by_clause; + } + + if($limit_offset_clause !== '') { + $sql.= ' '.$limit_offset_clause; + } + + try { + $rows = $this->model->query_bind($sql, $params, 'object'); + return $rows; + } catch (Exception $e) { + $this->api_manager_error(400, $e); + } + } + + private function extract_where_data($submitted_params) { + //returns a WHERE clause and an array of params + $params = []; + $counter = 0; + $ignore_keys = array('orderBy', 'order_by', 'limit', 'offset', 'ixd'); + $where_clause = ''; + foreach($submitted_params as $key => $value) { + $key_bits = explode(' ', trim($key)); + //ignore limit, offset etc... + if(in_array($key_bits[0], $ignore_keys)) { + continue; + } + + $conjunction = ($where_clause === '') ? 'WHERE' : (strtoupper($key_bits[0]) === 'OR' ? 'OR' : 'AND'); + $first_three = substr($key, 0, 3); + if (strtoupper($first_three) === 'OR ') { + $key = substr($key, 3); + $key_bits = explode(' ', trim($key)); //must be re-established, having dealt with 'OR' scenario + } + + $column = $key_bits[0]; + + if(count($key_bits)>1) { + $last_bit = $key_bits[count($key_bits)-1]; + $operator = ($last_bit === '!') ? '!=' : $operator = $last_bit; + } else { + $operator = '='; + } + + $counter++; + $property_name = 'arg'.$counter; + $where_clause.= $conjunction.' '.$column.$operator.'? '; + $params[] = $value; + } + + $where_data['where_clause'] = trim($where_clause); + $where_data['params'] = $params; + return $where_data; + } + + private function extract_order_by_clause($submitted_params) { + foreach($submitted_params as $key => $value) { + if($key === 'orderBy' OR $key === 'order_by') { + $order_by_clause = 'ORDER BY '.$value; + return $order_by_clause; + } + } + + $order_by_clause = ''; + return $order_by_clause; + } + + private function extract_limit_offset_clause($submitted_params) { + $limit_offset_clause = 'LIMIT [offset], [limit]'; + $limit = null; + $offset = null; + + foreach ($submitted_params as $key => $value) { + if (strtolower($key) === 'limit' && is_numeric($value)) { + $limit = (int) $value; + } elseif (strtolower($key) === 'offset' && is_numeric($value)) { + $offset = (int) $value; + } + } + + if ($limit === null && $offset === null) { + $limit_offset_clause = ''; + } else { + $limit_offset_clause = str_replace('[limit]', $limit ?? '18446744073709551615', $limit_offset_clause); + $limit_offset_clause = str_replace('[offset]', $offset ?? 0, $limit_offset_clause); + } + + return $limit_offset_clause; + } + + private function clean_output($output) { + $allowed_keys = ['body', 'code', 'token']; + return array_intersect_key($output, array_flip($allowed_keys)); + } + + public function attempt_serve_standard_endpoint($endpoint_index) { + $standard_endpoints = $this->get_standard_endpoints(); + $target_endpoint = $standard_endpoints[$endpoint_index]; + $request_name = $target_endpoint['request_name']; + $target_method = strtolower(url_title($request_name)); + $target_method = str_replace('-', '_', $target_method); + $this->$target_method(); + } + + public function attempt_find_endpoint_index() { + + $request_type = $this->get_request_type(); + $current_url = remove_query_string(current_url()); + + $first_segment = remove_query_string(segment(1)); + $second_segment = remove_query_string(segment(2)); + $ditch = $second_segment === '' ? BASE_URL.$first_segment : BASE_URL.$first_segment.'/'; + $resource_path = str_replace($ditch, '', $current_url); + + $segments = explode('/', $resource_path); + foreach ($segments as &$segment) { + if (is_numeric($segment)) { + $segment = '{id}'; + } + } + + $resource_path = implode('/', $segments); + + if (substr($resource_path, -1) === '/') { + $resource_path = substr($resource_path, 0, -1); + } + + $standard_endpoints = $this->get_standard_endpoints(); + $resource_path = str_replace('%7Bid%7D', '{id}', $resource_path); + + foreach ($standard_endpoints as $i => $standard_endpoint) { + $target_request_type = $standard_endpoint['request_type']; + $restful_identifier = $standard_endpoint['restful_identifier']; + + if(($resource_path === $restful_identifier) && ($request_type === $target_request_type)) { + $target_endpoint_index = $i; + break; + } + } + + if(!isset($target_endpoint_index)) { + $target_endpoint_index = ''; + } + + return $target_endpoint_index; + } + + private function get_target_endpoint($table_name, $endpoint_name) { + $file_path = APPPATH . 'modules/' . $table_name . '/assets/api.json'; + + if (!file_exists($file_path)) { + if (segment(1) !== 'api') { + load('error_404'); + die(); + } + + $this->api_manager_error(404, 'Endpoint settings not found!'); + } + + $endpoints = json_decode(file_get_contents($file_path), true); + $input['target_endpoint'] = $endpoints[$endpoint_name] ?? false; + + if ($input['target_endpoint'] === false) { + if (segment(1) !== 'api') { + load('error_404'); + die(); + } + + $this->api_manager_error(404, 'Endpoint settings not found!'); + } + + //token auth (make sure allowed) + $input['token'] = $this->make_sure_allowed($input['target_endpoint'], $table_name); + $input['module_name'] = $table_name; + $input['endpoint'] = $endpoint_name; + + //read query params from the URL + $request_type = strtoupper($input['target_endpoint']['request_type']); + $enable_params = $input['target_endpoint']['enableParams'] ?? false; + + if (($enable_params === true) && ($endpoint_name !== 'Insert Batch')) { + + if($request_type === 'GET') { + $query_params = $this->fetch_query_params_from_url(); + $input['params']= $this->reduce_query_params($query_params); + } else { + //attemp get params from post + $query_params = $this->fetch_query_params_from_post(); + $input['params']= $this->reduce_query_params($query_params); + } + + } + + if(!isset($input['params'])) { + $input['params'] = []; + } + + return $input; + } + + private function make_sure_allowed($target_endpoint, $table_name) { + + if (!isset($target_endpoint['authorization'])) { + $msg = 'Endpoint not activated since no authorization rules have been declared.'; + $this->api_manager_error(412, $msg); + } + + //attempt get user token + $this->module('trongate_tokens'); + $token = (isset($_SERVER['HTTP_TRONGATETOKEN']) ? $_SERVER['HTTP_TRONGATETOKEN'] : false); + + if ((($token === '') || ($token === false)) && (segment(1) !== 'api')) { + //attempt to fetch a token from cookie or session data + $token = $this->trongate_tokens->_attempt_get_valid_token(); + } + + $endpoint_auth_rules = $target_endpoint['authorization']; + $var_type = gettype($endpoint_auth_rules); + + if ($var_type === 'string' && $endpoint_auth_rules === '*') { + return $token; // wide open endpoint + } + + if ($token === '' || $token === false) { + $this->api_manager_error(401, 'Invalid token.'); + } + + //attempt fetch user object from the token + $trongate_user = $this->trongate_tokens->_get_user_obj($token); + + if($trongate_user === false) { + // Test for aaa token + $allowed = $this->test_for_aaa_token($token); + if($allowed === true) { + return $token; //aaa token submitted - access allowed + } else { + $this->api_manager_error(401, 'Invalid token.'); + } + } + + //let's go through all of the authoriation rules and attempt to pass ONE of them + $user_level = $trongate_user->user_level ?? ''; + $trongate_user_id = $trongate_user->trongate_user_id ?? ''; + $trongate_user_code = $trongate_user->trongate_user_code ?? ''; + + //role based authorization + if (isset($endpoint_auth_rules['roles'])) { + if (in_array($user_level, $endpoint_auth_rules['roles'])) { + return $token; //match for allowed user role! + } + } + + //id based authorization + if (isset($endpoint_auth_rules['userIds'])) { + if (in_array($trongate_user_id, $endpoint_auth_rules['userIds'])) { + return $token; //match for allowed trongate_user_id + } + } + + if(segment(1) === 'api') { + + //user id segment authorization (only for Trongate API paths!) + if (isset($endpoint_auth_rules['userIdSegment'])) { + $target_value = segment($endpoint_auth_rules['userIdSegment']); + if ($trongate_user_id === $target_value) { + return $token; //match for trongate_user_id on target segment + } + } + + //user code segment authorization (only for Trongate API paths!) + if (isset($endpoint_auth_rules['userCodeSegment'])) { + $target_value = segment($endpoint_auth_rules['userCodeSegment']); + if ($trongate_user_code === $target_value) { + return $token; //match for trongate_user_code on target segment + } + } + + //user owned segment authorization (only for Trongate API paths!) + if (isset($endpoint_auth_rules['userOwnedSegment'])) { + $test_data['table_name'] = $table_name; + $test_data['trongate_user_id'] = $trongate_user_id; + $test_data['user_owned_settings'] = $endpoint_auth_rules['userOwnedSegment']; + $this->run_user_owned_test($test_data); //true or false + return $token; + } + + } + + //safety net + $this->api_manager_error(401, 'Invalid token.'); + } + + private function test_for_aaa_token($token) { + $token_obj = $this->model->get_one_where('token', $token, 'trongate_tokens'); + $code = $token_obj->code ?? ''; + $allowed = $code === 'aaa' ? true : false; + return $allowed; + } + + private function run_user_owned_test($test_data) { + $column = $test_data['user_owned_settings']['column']; + $value = segment($test_data['user_owned_settings']['segmentNum']); + $target_table = $test_data['table_name']; + $record = $this->model->get_one_where($column, $value, $target_table); + + if ($record === false) { + $this->api_manager_error(401, 'Invalid token.'); + } else { + + $trongate_user_id = $test_data['trongate_user_id'] ?? 0; + $target_trongate_user_id = $record->trongate_user_id ?? 0; + settype($trongate_user_id, 'int'); + settype($target_trongate_user_id, 'int'); + + if ($trongate_user_id === $target_trongate_user_id && ($trongate_user_id>0)) { + return; //match for user owned segment + } else { + $this->api_manager_error(401, 'Invalid token.'); + } + + } + } + + private function fetch_query_params_from_url() { + $query_str = parse_url(urldecode(current_url()), PHP_URL_QUERY); + $query_params = []; + $query_str_bits = explode('&', $query_str); + $operators = $this->operators; + + foreach($query_str_bits as $query_str_bit) { + $row_data = $this->extract_query_param($query_str_bit, $operators); + if($row_data !== false) { + $query_params[] = $row_data; + } + } + + return $query_params; + } + + private function fetch_query_params_from_post() { + $query_params = []; + + //get posted params + $post = file_get_contents('php://input'); + + if($post === '') { + return $query_params; + } + + $posted_args = json_decode($post, true); + $operators = $this->operators; + + if(!isset($posted_args)) { + $this->api_manager_error(400, 'Invalid JSON!'); + } + + foreach($posted_args as $key => $value) { + $key = str_replace('>=', ' >=', $key); + $key = str_replace('<=', ' <=', $key); + $key = str_replace('!', ' !', $key); + $key = str_replace('<', ' <', $key); + $key = str_replace('>', ' >', $key); + + $unwanted_chars = array('or ', 'OR ' , '!', 'and ', 'AND ', '!=', '>', '<', '>=', '<='); + $key_string = $key; + foreach ($unwanted_chars as $char) { + $key_string = str_replace($char, '', $key_string); + } + + $row_data['key'] = trim($key_string); + + //figure out what the operator is + $key_bits = explode(' ', trim($key)); + if(count($key_bits) === 1) { + $row_data['operator'] = '='; + } else { + $row_data['operator'] = '='; + $key = str_replace('!', '!=', $key); + $operators_keys = array_keys($operators); + + foreach ($operators_keys as $needle) { + if (strpos($key, $needle) !== false) { + $row_data['operator'] = $needle; + break; + } + } + } + + if(strlen($key)>3) { + $first_three = substr($key, 0, 3); + if(strtoupper($first_three === 'OR ')) { + $row_data['key'] = 'OR '.$row_data['key']; + } + } + + $row_data['value'] = trim($value); + $query_params[] = $row_data; + } + + return $query_params; + } + + private function extract_query_param($query_str_bit, $operators) { + //build a bit list of all possible operators + foreach($operators as $operator_plain => $operator_encoded) { + $relevant_operator = $operator_encoded; + $str_bits = explode($operator_encoded, $query_str_bit); + + if(count($str_bits) !== 2) { + $str_bits = explode($operator_plain, $query_str_bit); + $relevant_operator = $operator_plain; + } + + if(count($str_bits) === 2) { + $row_data['key'] = $str_bits[0]; + $row_data['operator'] = $relevant_operator; + $row_data['value'] = $str_bits[1]; + return $row_data; + } + } + return false; + } + + function reduce_query_params($query_params) { + //reduce query params to simple key/value pairs + $params = []; + foreach($query_params as $query_param) { + $param_key = $query_param['key'] ?? ''; + $param_operator = $query_param['operator'] ?? ''; + $param_value = $query_param['value'] ?? ''; + + if($param_operator === '=') { + $key = $param_key; + } else { + $param_operator = str_replace('!=', '!', $param_operator); + $param_operator = str_replace('%21=', '!', $param_operator); + $param_operator = str_replace('%3C', '<', $param_operator); + $param_operator = str_replace('%3E', '<', $param_operator); + $key = $param_key.' '.$param_operator; + } + + $params[$key] = $param_value; + + } + return $params; + } + + private function api_manager_error($response_status_code, $error_msg) { + http_response_code($response_status_code); + if (strtolower(ENV) === 'dev') { + echo $error_msg; + } + die(); + } + + private function get_standard_endpoints() { + $standard_endpoints = [ + [ + 'request_name' => 'Get', + 'request_type' => 'GET', + 'restful_identifier' => '', + 'url_segments' => 'api/get/[table]' + ], + [ + 'request_name' => 'Search', + 'request_type' => 'POST', + 'restful_identifier' => 'search', + 'url_segments' => 'api/search/[table]' + ], + [ + 'request_name' => 'Find One', + 'request_type' => 'GET', + 'restful_identifier' => '{id}', + 'url_segments' => 'api/get/[table]/{id}' + ], + [ + 'request_name' => 'Exists', + 'request_type' => 'GET', + 'restful_identifier' => '{id}/exists', + 'url_segments' => 'api/exists/[table]/{id}' + ], + [ + 'request_name' => 'Count', + 'request_type' => 'GET', + 'restful_identifier' => 'count', + 'url_segments' => 'api/count/[table]' + ], + [ + 'request_name' => 'Count By Post', + 'request_type' => 'POST', + 'restful_identifier' => 'count', + 'url_segments' => 'api/count/[table]' + ], + [ + 'request_name' => 'Create', + 'request_type' => 'POST', + 'restful_identifier' => '', + 'url_segments' => 'api/create/[table]' + ], + [ + 'request_name' => 'Insert Batch', + 'request_type' => 'POST', + 'restful_identifier' => 'insert', + 'url_segments' => 'api/batch/[table]' + ], + [ + 'request_name' => 'Update', + 'request_type' => 'PUT', + 'restful_identifier' => '{id}', + 'url_segments' => 'api/update/[table]/{id}' + ], + [ + 'request_name' => 'Destroy', + 'request_type' => 'DELETE', + 'restful_identifier' => '', + 'url_segments' => 'api/destroy/[table]' + ], + [ + 'request_name' => 'Delete One', + 'request_type' => 'DELETE', + 'restful_identifier' => '{id}', + 'url_segments' => 'api/delete/[table]/{id}' + ], + ]; + return $standard_endpoints; + } +} \ No newline at end of file diff --git a/engine/tg_helpers/url_helper.php b/engine/tg_helpers/url_helper.php index ad677e9b..de638a65 100644 --- a/engine/tg_helpers/url_helper.php +++ b/engine/tg_helpers/url_helper.php @@ -14,6 +14,12 @@ function segment($num, $var_type = null) { return $value; } +function remove_query_string($string) { + $parts = explode("?", $string, 2); + return $parts[0]; +} + + function previous_url() { if (isset($_SERVER['HTTP_REFERER'])) { $url = $_SERVER['HTTP_REFERER']; @@ -50,7 +56,7 @@ function anchor($target_url, $text, $attributes = NULL, $additional_code = NULL) $text_type = gettype($text); - if ($text_type == 'boolean') { + if ($text_type === 'boolean') { return $target_url; } @@ -88,7 +94,7 @@ function nice_price($num) { * @return string The slugified version of the string. */ function url_title($value, $transliteration = true) { - if (extension_loaded('intl') && $transliteration == true) { + if (extension_loaded('intl') && $transliteration === true) { $transliterator = \Transliterator::create('Any-Latin; Latin-ASCII'); $value = $transliterator->transliterate($value); } @@ -157,7 +163,7 @@ function api_auth() { } } - if ($segments_match == true) { + if ($segments_match === true) { $token_validation_data['endpoint'] = $rule_name; $token_validation_data['module_name'] = $current_module; @@ -181,7 +187,7 @@ function api_auth() { } } - if ($validation_complete == false) { + if ($validation_complete === false) { http_response_code(401); echo "Invalid token."; die(); diff --git a/engine/views/api_explorer.php b/engine/views/api_explorer.php index b175f3b8..3d31c78d 100755 --- a/engine/views/api_explorer.php +++ b/engine/views/api_explorer.php @@ -1,602 +1,82 @@ - - API Explorer - - - - - + + + + + Trongate API Explorer - -
    -
    - -

    Token Not Set!

    -
    -

    - -

    -
    -
    -

    - -

    +
    +
    Trongate API Explorer
    +
    + +
    -
    - -
    -
    -
    -
    -

    - +
    + +
    +

    +
    +
    - - - - + + + + - - $endpoint) { - $endpoint_json = json_encode($endpoint); - - switch($endpoint['request_type']) { - case 'GET': - $btn_theme = 'green'; - break; - case 'POST': - $btn_theme = 'purple'; - break; - case 'PUT': - $btn_theme = 'deep-purple'; - break; - case 'DELETE': - $btn_theme = 'red'; - break; - default: - $btn_theme = 'green'; - break; + + $endpoint_row) { + $counter++; + $btn_class = 'btn-'.strtolower($endpoint_row['request_type']); + $btn_class.= ' open-api-tester'; + $btn_key = str_replace(' ', '-', strtolower($endpoint_name)); + $request_type = $endpoint_row['request_type']; + ?> + + + + + + + }'; - $endpoint['url_segments'] = str_replace($ditch, $replace, $endpoint['url_segments']); - $ditch = '{'; - $replace = '{'; - $endpoint['url_segments'] = str_replace($ditch, $replace, $endpoint['url_segments']); - ?> - - - - - - - + ?>
    Request TypeEndpoint NameURL segmentsDescriptionRequest TypeEndpoint NameURL SegmentsDescription
    + +
    -
    -
    + -
    -
    - - -
    -

    For full documentation and tutorials visit Trongate.io

    -
    + - - - + \ No newline at end of file diff --git a/engine/views/api_explorer_js.php b/engine/views/api_explorer_js.php new file mode 100644 index 00000000..5bdaf260 --- /dev/null +++ b/engine/views/api_explorer_js.php @@ -0,0 +1,271 @@ + + \ No newline at end of file diff --git a/engine/views/api_explorer_modal_js.php b/engine/views/api_explorer_modal_js.php new file mode 100644 index 00000000..f3115b1e --- /dev/null +++ b/engine/views/api_explorer_modal_js.php @@ -0,0 +1,781 @@ + \ No newline at end of file diff --git a/engine/views/api_explorer_style.php b/engine/views/api_explorer_style.php new file mode 100644 index 00000000..85a6b1c9 --- /dev/null +++ b/engine/views/api_explorer_style.php @@ -0,0 +1,382 @@ + \ No newline at end of file diff --git a/engine/views/highlight_errors.txt b/engine/views/highlight_errors.txt index f13fffa8..eb1217e9 100644 --- a/engine/views/highlight_errors.txt +++ b/engine/views/highlight_errors.txt @@ -52,7 +52,7 @@ function addErrorClasses(key, allFormFields) { for (var i = 0; i < allFormFields.length; i++) { if (allFormFields[i]['name'] == key) { let formFieldType = allFormFields[i]['type']; - if ((formFieldType == 'checkbox') || (formFieldType == 'radio')) { + if ((formFieldType === 'checkbox') || (formFieldType === 'radio')) { let parentContainer = allFormFields[i].closest('div'); parentContainer.classList.add('form-field-validation-error'); parentContainer.style.textIndent = '7px'; From 4504c2e8e4bbfd49c68d212b48d45bc0c779e063 Mon Sep 17 00:00:00 2001 From: David Connelly Date: Mon, 3 Apr 2023 15:15:07 +0100 Subject: [PATCH 079/100] correction to fetch_query_params_from_url() function on Standard_endpoints.php - now forcing $query_str to be of type 'string' --- engine/Standard_endpoints.php | 1 + 1 file changed, 1 insertion(+) diff --git a/engine/Standard_endpoints.php b/engine/Standard_endpoints.php index 3739a5e1..da1d5e27 100644 --- a/engine/Standard_endpoints.php +++ b/engine/Standard_endpoints.php @@ -899,6 +899,7 @@ private function run_user_owned_test($test_data) { private function fetch_query_params_from_url() { $query_str = parse_url(urldecode(current_url()), PHP_URL_QUERY); + settype($query_str, 'string'); $query_params = []; $query_str_bits = explode('&', $query_str); $operators = $this->operators; From e42063755699c9921f4d86ef55236ce8d254a873 Mon Sep 17 00:00:00 2001 From: David Connelly Date: Mon, 3 Apr 2023 15:44:59 +0100 Subject: [PATCH 080/100] Correction to Trongate_administrators - update ID now being converted to int. --- .../controllers/Trongate_administrators.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/trongate_administrators/controllers/Trongate_administrators.php b/modules/trongate_administrators/controllers/Trongate_administrators.php index e1cf89b1..8333d545 100755 --- a/modules/trongate_administrators/controllers/Trongate_administrators.php +++ b/modules/trongate_administrators/controllers/Trongate_administrators.php @@ -315,7 +315,7 @@ function _verify_hash($plain_text_str, $hashed_string) { function username_check($str) { //NOTE: You may wish to add other rules of your own here! - $update_id = segment(3); + $update_id = (int) segment(3); $result = $this->model->get_one_where('username', $str, 'trongate_administrators'); $error_msg = 'The username that you submitted is not available.'; From 0340e2b074532fa1cf5f647e182d7ec0c194a7c3 Mon Sep 17 00:00:00 2001 From: David Connelly Date: Mon, 10 Apr 2023 11:52:29 +0100 Subject: [PATCH 081/100] Added fallback that handles 'Get By Post' requests as if they are 'Search' requests --- engine/Standard_endpoints.php | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/engine/Standard_endpoints.php b/engine/Standard_endpoints.php index da1d5e27..f84033f8 100644 --- a/engine/Standard_endpoints.php +++ b/engine/Standard_endpoints.php @@ -734,7 +734,17 @@ private function get_target_endpoint($table_name, $endpoint_name) { } $endpoints = json_decode(file_get_contents($file_path), true); - $input['target_endpoint'] = $endpoints[$endpoint_name] ?? false; + + if ($endpoint_name === 'Search') { + $input['target_endpoint'] = $endpoints[$endpoint_name] ?? false; + + // If the 'Search' endpoint is not found, try 'Get By Post' as fallback + if ($input['target_endpoint'] === false) { + $input['target_endpoint'] = $endpoints['Get By Post'] ?? false; + } + } else { + $input['target_endpoint'] = $endpoints[$endpoint_name] ?? false; + } if ($input['target_endpoint'] === false) { if (segment(1) !== 'api') { From 8b00e5ae72a13281ba76cfa96582ef12ab8389ef Mon Sep 17 00:00:00 2001 From: David Connelly Date: Mon, 10 Apr 2023 11:54:53 +0100 Subject: [PATCH 082/100] Renamed 'Get By Post' API endpoints to 'Search'. --- modules/trongate_administrators/assets/api.json | 2 +- modules/trongate_comments/assets/api.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/trongate_administrators/assets/api.json b/modules/trongate_administrators/assets/api.json index 2d3b804b..be645786 100755 --- a/modules/trongate_administrators/assets/api.json +++ b/modules/trongate_administrators/assets/api.json @@ -10,7 +10,7 @@ ] } }, - "Get By Post": { + "Search": { "url_segments": "api/get/trongate_administrators", "request_type": "POST", "description": "Fetch rows from table using POST request.", diff --git a/modules/trongate_comments/assets/api.json b/modules/trongate_comments/assets/api.json index 754a4db2..e41b5150 100755 --- a/modules/trongate_comments/assets/api.json +++ b/modules/trongate_comments/assets/api.json @@ -1,5 +1,5 @@ { - "Get By Post": { + "Search": { "url_segments": "api/get/trongate_comments", "request_type": "POST", "description": "Fetch rows from table using POST request.", From 486490efaf15bd6ff6c2d3ca9db8685e2d9d464c Mon Sep 17 00:00:00 2001 From: David Connelly Date: Mon, 10 Apr 2023 13:06:58 +0100 Subject: [PATCH 083/100] Correction to Trongate filezone (not displaying uploaded images correctly). Also added validate_token function to Api.php - this simplifies a previous effort. --- engine/Api.php | 11 +++++++++++ engine/Standard_endpoints.php | 2 +- engine/tg_helpers/url_helper.php | 2 +- modules/trongate_filezone/views/uploader.php | 3 +-- 4 files changed, 14 insertions(+), 4 deletions(-) diff --git a/engine/Api.php b/engine/Api.php index 641697f8..70fdb43c 100755 --- a/engine/Api.php +++ b/engine/Api.php @@ -212,4 +212,15 @@ public function delete() { //POST or DELETE $se->delete_one(); } + public function validate_token($token_validation_data) { + //get an array of ALL the rules for this endpoint + $target_endpoint = $token_validation_data['module_endpoints'][$token_validation_data['endpoint']]; + $table_name = segment(1) === 'api' || segment(1) === 'trongate_filezone' ? segment(3) : segment(1); + $table_name = remove_query_string($table_name); + require_once('Standard_endpoints.php'); + $se = new Standard_endpoints(); + $token = $se->make_sure_allowed($target_endpoint, $table_name); + return $token; + } + } \ No newline at end of file diff --git a/engine/Standard_endpoints.php b/engine/Standard_endpoints.php index f84033f8..35718be3 100644 --- a/engine/Standard_endpoints.php +++ b/engine/Standard_endpoints.php @@ -784,7 +784,7 @@ private function get_target_endpoint($table_name, $endpoint_name) { return $input; } - private function make_sure_allowed($target_endpoint, $table_name) { + public function make_sure_allowed($target_endpoint, $table_name) { if (!isset($target_endpoint['authorization'])) { $msg = 'Endpoint not activated since no authorization rules have been declared.'; diff --git a/engine/tg_helpers/url_helper.php b/engine/tg_helpers/url_helper.php index de638a65..dcc513bc 100644 --- a/engine/tg_helpers/url_helper.php +++ b/engine/tg_helpers/url_helper.php @@ -174,7 +174,7 @@ function api_auth() { if (file_exists($api_class_location)) { include_once $api_class_location; $api_helper = new Api; - $api_helper->_validate_token($token_validation_data); + $api_helper->validate_token($token_validation_data); $validation_complete = true; } } diff --git a/modules/trongate_filezone/views/uploader.php b/modules/trongate_filezone/views/uploader.php index 7b7129e8..bc3dfe8a 100755 --- a/modules/trongate_filezone/views/uploader.php +++ b/modules/trongate_filezone/views/uploader.php @@ -7,8 +7,7 @@
    From 7ea8ee87c4367c3810930ea2f44695515ddef83a Mon Sep 17 00:00:00 2001 From: David Connelly Date: Sun, 16 Apr 2023 16:23:24 +0100 Subject: [PATCH 084/100] 1) Added type hinting and return types to the Trongate.php class. Also, 2). simplified view() method on Trongate.php. Finally, changed 'NULL' to 'null' (lowercase!) throughout the engine. --- engine/Model.php | 28 +++--- engine/Modules.php | 2 +- engine/Pagination.php | 6 +- engine/Standard_endpoints.php | 2 +- engine/Template.php | 6 +- engine/Trongate.php | 125 +++++++++--------------- engine/ignition.php | 4 +- engine/tg_helpers/file_helper.php | 2 +- engine/tg_helpers/flashdata_helper.php | 2 +- engine/tg_helpers/form_helper.php | 34 +++---- engine/tg_helpers/url_helper.php | 2 +- engine/tg_helpers/validation_helper.php | 2 +- 12 files changed, 89 insertions(+), 126 deletions(-) diff --git a/engine/Model.php b/engine/Model.php index da8e74cf..cc679026 100755 --- a/engine/Model.php +++ b/engine/Model.php @@ -14,7 +14,7 @@ class Model { private $query_caveat = 'The query shown above is how the query would look before binding.'; private $current_module; - public function __construct($current_module = NULL) { + public function __construct($current_module = null) { if (DATABASE == '') { return; @@ -110,7 +110,7 @@ protected function _get_all_tables() { return $tables; } - public function get($order_by=NULL, $target_tbl=NULL, $limit=NULL, $offset=NULL) { + public function get($order_by=null, $target_tbl=null, $limit=null, $offset=null) { $order_by = (!isset($order_by)) ? 'id' : $order_by; @@ -137,7 +137,7 @@ public function get($order_by=NULL, $target_tbl=NULL, $limit=NULL, $offset=NULL) return $query; } - public function get_where_custom($column, $value, $operator = '=', $order_by = 'id', $target_tbl = NULL, $limit = NULL, $offset = NULL) { + public function get_where_custom($column, $value, $operator = '=', $order_by = 'id', $target_tbl = null, $limit = null, $offset = null) { if (!isset($target_tbl)) { $target_tbl = $this->get_table_from_url(); @@ -175,7 +175,7 @@ public function get_where_custom($column, $value, $operator = '=', $order_by = ' } //fetch a single record - public function get_where($id, $target_tbl = NULL) { + public function get_where($id, $target_tbl = null) { $data['id'] = (int) $id; @@ -198,7 +198,7 @@ public function get_where($id, $target_tbl = NULL) { } //fetch a single record (alternative version) - public function get_one_where($column, $value, $target_tbl = NULL) { + public function get_one_where($column, $value, $target_tbl = null) { $data[$column] = $value; if (!isset($target_tbl)) { @@ -219,7 +219,7 @@ public function get_one_where($column, $value, $target_tbl = NULL) { } } - public function get_many_where($column, $value, $target_tbl = NULL) { + public function get_many_where($column, $value, $target_tbl = null) { if (!isset($target_tbl)) { $target_tbl = $this->get_table_from_url(); @@ -233,7 +233,7 @@ public function get_many_where($column, $value, $target_tbl = NULL) { return $query; } - public function count($target_tbl = NULL) { + public function count($target_tbl = null) { //return number of rows on a table if (!isset($target_tbl)) { @@ -255,7 +255,7 @@ public function count($target_tbl = NULL) { } } - public function count_where($column, $value, $operator = '=', $order_by = 'id', $target_tbl = NULL, $limit = NULL, $offset = NULL) { + public function count_where($column, $value, $operator = '=', $order_by = 'id', $target_tbl = null, $limit = null, $offset = null) { //return number of rows on table (with query customisation) $query = $this->get_where_custom($column, $value, $operator, $order_by, $target_tbl, $limit, $offset); @@ -263,7 +263,7 @@ public function count_where($column, $value, $operator = '=', $order_by = 'id', return $num_rows; } - public function count_rows($column, $value, $target_tbl = NULL) { + public function count_rows($column, $value, $target_tbl = null) { //simplified version of count_where (accepts one condition) if (!isset($target_tbl)) { @@ -285,7 +285,7 @@ public function count_rows($column, $value, $target_tbl = NULL) { } } - public function get_max($target_tbl = NULL) { + public function get_max($target_tbl = null) { if (!isset($target_tbl)) { $target_tbl = $this->get_table_from_url(); @@ -307,7 +307,7 @@ public function get_max($target_tbl = NULL) { } } - public function show_query($query, $data, $caveat = NULL) { + public function show_query($query, $data, $caveat = null) { $keys = array(); $values = $data; $named_params = true; @@ -376,7 +376,7 @@ public function show_query($query, $data, $caveat = NULL) { get_table_from_url(); @@ -402,7 +402,7 @@ public function insert($data, $target_tbl = NULL) { return $id; } - public function update($update_id, $data, $target_tbl = NULL) { + public function update($update_id, $data, $target_tbl = null) { if (!isset($target_tbl)) { $target_tbl = $this->get_table_from_url(); @@ -427,7 +427,7 @@ public function update($update_id, $data, $target_tbl = NULL) { $this->prepare_and_execute($sql, $data); } - public function delete($id, $target_tbl = NULL) { + public function delete($id, $target_tbl = null) { if (!isset($target_tbl)) { $target_tbl = $this->get_table_from_url(); diff --git a/engine/Modules.php b/engine/Modules.php index 0e97be35..d0343940 100755 --- a/engine/Modules.php +++ b/engine/Modules.php @@ -1,7 +1,7 @@ ERROR: Data must be passed into the pagination class in order for it to work. Please refer to documentation.'); @@ -168,7 +168,7 @@ static public function display($data=NULL) { if (isset($data['record_name_plural'])) { $record_name_plural = $data['record_name_plural']; } else { - $record_name_plural = NULL; + $record_name_plural = null; } $pagination_data['showing_statement'] = self::get_showing_statement($limit, $current_page, $total_rows, $record_name_plural); @@ -331,7 +331,7 @@ static public function get_settings_default() { return $settings; } - static public function get_showing_statement($limit, $current_page, $total_rows, $record_name_plural=NULL) { + static public function get_showing_statement($limit, $current_page, $total_rows, $record_name_plural=null) { $offset = ($current_page * $limit) - $limit; diff --git a/engine/Standard_endpoints.php b/engine/Standard_endpoints.php index 35718be3..df917b1b 100644 --- a/engine/Standard_endpoints.php +++ b/engine/Standard_endpoints.php @@ -260,7 +260,7 @@ public function create() { } - function make_sure_columns_exist($table_name, $params, $valid_columns=NULL) { + function make_sure_columns_exist($table_name, $params, $valid_columns=null) { if(!isset($valid_columns)) { $valid_columns = $this->get_all_columns($table_name); diff --git a/engine/Template.php b/engine/Template.php index 70a8afb2..f8f3fed5 100755 --- a/engine/Template.php +++ b/engine/Template.php @@ -17,7 +17,7 @@ static public function get_view_module() { return $view_module; } - static public function display($data=NULL) { + static public function display($data=null) { if (!isset($data['view_module'])) { $data['view_module'] = self::get_view_module(); @@ -31,12 +31,12 @@ static public function display($data=NULL) { self::attempt_include($file_path, $data); } - static public function partial($file_name, $data=NULL) { + static public function partial($file_name, $data=null) { $file_path = APPPATH.'templates/views/'.$file_name.'.php'; self::attempt_include($file_path, $data); } - static private function attempt_include($file_path, $data=NULL) { + static private function attempt_include($file_path, $data=null) { if (file_exists($file_path)) { diff --git a/engine/Trongate.php b/engine/Trongate.php index 666733be..074a9eef 100755 --- a/engine/Trongate.php +++ b/engine/Trongate.php @@ -3,24 +3,23 @@ class Trongate { use Dynamic_properties; - private $model; - protected $modules; - protected $url; - protected $module_name; - protected $parent_module = ''; - protected $child_module = ''; - - public function __construct($module_name=NULL) { + protected Modules $modules; + private ?Model $model; + protected ?string $module_name; + protected string $parent_module = ''; + protected string $child_module = ''; + + public function __construct(?string $module_name=null) { $this->module_name = $module_name; $this->modules = new Modules; } - public function load($helper) { + public function load(string $helper): void { require_once 'tg_helpers/'.$helper.'.php'; $this->$helper = new $helper; } - public function template($template_name, $data=NULL) { + public function template(string $template_name, array $data): void { $template_controller_path = '../templates/controllers/Templates.php'; require_once $template_controller_path; @@ -40,7 +39,7 @@ public function template($template_name, $data=NULL) { } } - public function module($target_module) { + public function module(string $target_module): void { $target_controller = ucfirst($target_module); $target_controller_path = '../modules/'.$target_module.'/controllers/'.$target_controller.'.php'; @@ -57,7 +56,7 @@ public function module($target_module) { $this->$target_module = new $target_module($target_module); } - private function get_child_module($target_module) { + private function get_child_module(string $target_module): string|null { $child_module_path = false; $bits = explode('-', $target_module); @@ -76,98 +75,62 @@ private function get_child_module($target_module) { return $child_module; } - protected function view($view, $data = [], $return_as_str=NULL) { - - if ((isset($return_as_str)) || (gettype($data) == 'boolean')) { - $return_as_str = true; - } else { - $return_as_str = false; - } - - if (($this->parent_module !== '') && ($this->child_module !== '')) { - //load view from child module - if ($return_as_str == true) { - // Return output as string - ob_start(); - $this->load_child_view($view, $data); - $output = ob_get_clean(); + protected function view(string $view, array $data = [], bool $return_as_str = false): string|null { + if ($this->parent_module !== '' && $this->child_module !== '') { + // Load view from child module + $output = $this->load_view_file($view, $data, $return_as_str); + if ($return_as_str) { return $output; - } else { - // Require child file - $this->load_child_view($view, $data); } - } else { - //normal view loading process - if (isset($data['view_module'])) { - $module_name = $data['view_module']; - } else { - $module_name = $this->module_name; - } - + // Normal view loading process + $module_name = $data['view_module'] ?? $this->module_name; extract($data); - - $view_path = APPPATH.'modules/'.$module_name.'/views/'.$view.'.php'; - - // Check for view file - if(file_exists($view_path)){ - - if ($return_as_str == true) { - // Return output as string - ob_start(); - require $view_path; - $output = ob_get_clean(); - return $output; - } else { - // Require view file - require $view_path; - } - - } else { - // No view exists + $view_path = APPPATH . 'modules/' . $module_name . '/views/' . $view . '.php'; + if (!file_exists($view_path)) { $view = str_replace('/', '/views/', $view); - $view_path = APPPATH.'modules/'.$view.'.php'; - - if(file_exists($view_path)){ - - if ($return_as_str == true) { - // Return output as string - ob_start(); - require $view_path; - $output = ob_get_clean(); - return $output; - } else { - // Require view file - require $view_path; - } - - } else { - throw new exception('view '.$view_path.' does not exist'); - } + $view_path = APPPATH . 'modules/' . $view . '.php'; + } + $output = $this->load_view_file($view_path, $data, $return_as_str); + if ($return_as_str) { + return $output; } } } - private function load_child_view($view, $data) { + protected function load_view_file(string $view_path, array $data, bool $return_as_str): string|null { + if (!file_exists($view_path)) { + throw new Exception('View ' . $view_path . ' does not exist'); + } + if ($return_as_str) { + ob_start(); + require $view_path; + return ob_get_clean(); + } else { + require $view_path; + } + } + + private function load_child_view(string $view, array $data): void { extract($data); - $view_path = APPPATH.'modules/'.$this->parent_module.'/'.$this->child_module.'/views/'.$view.'.php'; + $view_path = APPPATH . 'modules/' . $this->parent_module . '/' . $this->child_module . '/views/' . $view . '.php'; // Check for view file - if(file_exists($view_path)){ + if (file_exists($view_path)) { // Require view file require_once $view_path; } else { // No view exists - throw new exception('view '.$view_path.' does not exist'); + throw new Exception('view ' . $view_path . ' does not exist'); } } - public function upload_picture($data) { + public function upload_picture(array $data): array|null { $uploaded_file_info = $this->img_helper->upload($data); return $uploaded_file_info; } - public function upload_file($data) { + public function upload_file(array $data): array|null { $uploaded_file_info = $this->file_helper->upload($data); return $uploaded_file_info; } diff --git a/engine/ignition.php b/engine/ignition.php index 14a7412a..ae99fba7 100644 --- a/engine/ignition.php +++ b/engine/ignition.php @@ -21,7 +21,7 @@ return false; }); -function load($template_file, $data=NULL) { +function load($template_file, $data=null) { //load template view file if (isset(THEMES[$template_file])) { $theme_dir = THEMES[$template_file]['dir']; @@ -45,7 +45,7 @@ function load($template_file, $data=NULL) { } } -function get_segments($ignore_custom_routes=NULL) { +function get_segments($ignore_custom_routes=null) { //figure out how many segments need to be ditched $pseudo_url = str_replace('://', '', BASE_URL); diff --git a/engine/tg_helpers/file_helper.php b/engine/tg_helpers/file_helper.php index 3744e385..d19a37b3 100644 --- a/engine/tg_helpers/file_helper.php +++ b/engine/tg_helpers/file_helper.php @@ -4,7 +4,7 @@ class File_helper { public function upload($config) { //declare all inbound variables - $destination = $config['destination'] ?? NULL; + $destination = $config['destination'] ?? null; $target_module = $config['target_module'] ?? segment(1); $upload_to_module = $config['upload_to_module'] ?? false; $make_rand_name = $config['make_rand_name'] ?? false; diff --git a/engine/tg_helpers/flashdata_helper.php b/engine/tg_helpers/flashdata_helper.php index 23b43d53..b133019c 100644 --- a/engine/tg_helpers/flashdata_helper.php +++ b/engine/tg_helpers/flashdata_helper.php @@ -3,7 +3,7 @@ function set_flashdata($msg) { $_SESSION['flashdata'] = $msg; } -function flashdata($opening_html=NULL, $closing_html=NULL) { +function flashdata($opening_html=null, $closing_html=null) { if (isset($_SESSION['flashdata'])) { diff --git a/engine/tg_helpers/form_helper.php b/engine/tg_helpers/form_helper.php index ba98954d..5f326800 100644 --- a/engine/tg_helpers/form_helper.php +++ b/engine/tg_helpers/form_helper.php @@ -1,5 +1,5 @@ ', ' enctype="multipart/form-data">', $html); return $html; @@ -81,7 +81,7 @@ function get_attributes_str($attributes) { return $attributes_str; } -function form_label($label_text, $attributes=NULL, $additional_code=NULL) { +function form_label($label_text, $attributes=null, $additional_code=null) { $extra = ''; if (isset($attributes)) { @@ -95,7 +95,7 @@ function form_label($label_text, $attributes=NULL, $additional_code=NULL) { return ''.$label_text.''; } -function form_input($name, $value=NULL, $attributes=NULL, $additional_code=NULL) { +function form_input($name, $value=null, $attributes=null, $additional_code=null) { $extra = ''; if (!isset($value)) { $value = ''; @@ -112,31 +112,31 @@ function form_input($name, $value=NULL, $attributes=NULL, $additional_code=NULL) return ''; } -function form_number($name, $value=NULL, $attributes=NULL, $additional_code=NULL) { +function form_number($name, $value=null, $attributes=null, $additional_code=null) { $html = form_input($name, $value, $attributes, $additional_code); $html = str_replace('type="text"', 'type="number"', $html); return $html; } -function form_password($name, $value=NULL, $attributes=NULL, $additional_code=NULL) { +function form_password($name, $value=null, $attributes=null, $additional_code=null) { $html = form_input($name, $value, $attributes, $additional_code); $html = str_replace(' type="text" ', ' type="password" ', $html); return $html; } -function form_email($name, $value=NULL, $attributes=NULL, $additional_code=NULL) { +function form_email($name, $value=null, $attributes=null, $additional_code=null) { $html = form_input($name, $value, $attributes, $additional_code); $html = str_replace(' type="text" ', ' type="email" ', $html); return $html; } -function form_hidden($name, $value=NULL, $additional_code=NULL) { +function form_hidden($name, $value=null, $additional_code=null) { $html = form_input($name, $value, $additional_code); $html = str_replace(' type="text" ', ' type="hidden" ', $html); return $html; } -function form_textarea($name, $value=NULL, $attributes=NULL, $additional_code=NULL) { +function form_textarea($name, $value=null, $attributes=null, $additional_code=null) { $extra = ''; if (!isset($value)) { @@ -154,7 +154,7 @@ function form_textarea($name, $value=NULL, $attributes=NULL, $additional_code=NU return ''; } -function form_submit($name, $value=NULL, $attributes=NULL, $additional_code=NULL) { +function form_submit($name, $value=null, $attributes=null, $additional_code=null) { $extra = ''; if (!isset($value)) { @@ -173,13 +173,13 @@ function form_submit($name, $value=NULL, $attributes=NULL, $additional_code=NULL } -function form_button($name, $value=NULL, $attributes=NULL, $additional_code=NULL) { +function form_button($name, $value=null, $attributes=null, $additional_code=null) { $html = form_submit($name, $value, $attributes, $additional_code); $html = str_replace(' type="submit" ', ' type="button" ', $html); return $html; } -function form_radio($name, $value=NULL, $checked=NULL, $attributes=NULL, $additional_code=NULL) { +function form_radio($name, $value=null, $checked=null, $attributes=null, $additional_code=null) { $extra = ''; @@ -207,13 +207,13 @@ function form_radio($name, $value=NULL, $checked=NULL, $attributes=NULL, $additi return $html; } -function form_checkbox($name, $value=NULL, $checked=NULL, $attributes=NULL, $additional_code=NULL) { +function form_checkbox($name, $value=null, $checked=null, $attributes=null, $additional_code=null) { $html = form_radio($name, $value, $checked, $attributes, $additional_code); $html = str_replace(' type="radio" ', ' type="checkbox" ', $html); return $html; } -function form_dropdown($name, $options, $selected_key=NULL, $attributes=NULL, $additional_code=NULL) { +function form_dropdown($name, $options, $selected_key=null, $attributes=null, $additional_code=null) { $extra = ''; if (isset($attributes)) { @@ -246,14 +246,14 @@ function form_dropdown($name, $options, $selected_key=NULL, $attributes=NULL, $a return $html; } -function form_file_select($name, $attributes=NULL, $additional_code=NULL) { - $value = NULL; +function form_file_select($name, $attributes=null, $additional_code=null) { + $value = null; $html = form_input($name, $value, $attributes, $additional_code); $html = str_replace(' type="text" ', ' type="file" ', $html); return $html; } -function post($field_name, $clean_up=NULL) { +function post($field_name, $clean_up=null) { if (!isset($_POST[$field_name])) { $value = ''; } else { diff --git a/engine/tg_helpers/url_helper.php b/engine/tg_helpers/url_helper.php index dcc513bc..76d5f413 100644 --- a/engine/tg_helpers/url_helper.php +++ b/engine/tg_helpers/url_helper.php @@ -45,7 +45,7 @@ function redirect($target_url) { die(); } -function anchor($target_url, $text, $attributes = NULL, $additional_code = NULL) { +function anchor($target_url, $text, $attributes = null, $additional_code = null) { $str = substr($target_url, 0, 4); if ($str != 'http') { diff --git a/engine/tg_helpers/validation_helper.php b/engine/tg_helpers/validation_helper.php index ad539626..1530aefd 100644 --- a/engine/tg_helpers/validation_helper.php +++ b/engine/tg_helpers/validation_helper.php @@ -610,7 +610,7 @@ private function csrf_block_request() { } -function validation_errors($opening_html=NULL, $closing_html=NULL) { +function validation_errors($opening_html=null, $closing_html=null) { if (isset($_SESSION['form_submission_errors'])) { From 8ffe103420d4a8a94d99c6f92f6e97a5ef1d51ad Mon Sep 17 00:00:00 2001 From: Simon Field Date: Tue, 18 Apr 2023 23:31:18 +1000 Subject: [PATCH 085/100] Fixed type errors and added doc blocks Fixed some type errors when using later versions of PHP Also, added doc blocks --- engine/Trongate.php | 121 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 103 insertions(+), 18 deletions(-) diff --git a/engine/Trongate.php b/engine/Trongate.php index 074a9eef..7a386614 100755 --- a/engine/Trongate.php +++ b/engine/Trongate.php @@ -9,17 +9,37 @@ class Trongate { protected string $parent_module = ''; protected string $child_module = ''; - public function __construct(?string $module_name=null) { + public function __construct(?string $module_name = null) { $this->module_name = $module_name; $this->modules = new Modules; } + /** + * Load a helper class dynamically and instantiate it. + * + * @param string $helper The name of the helper class to load. + * + * @return void + * + * @throws Exception If the helper class file cannot be found or the class cannot be instantiated. + */ public function load(string $helper): void { - require_once 'tg_helpers/'.$helper.'.php'; + require_once 'tg_helpers/' . $helper . '.php'; $this->$helper = new $helper; } - public function template(string $template_name, array $data): void { + /** + * Loads a template controller file, instantiates the corresponding object, and calls + * the specified template method with the given data. + * + * @param string $template_name The name of the template method to call. + * @param array $data The data to pass to the template method. + * + * @return void + * + * @throws Exception If the template controller file cannot be found or the template method does not exist. + */ + public function template(string $template_name, array $data = []): void { $template_controller_path = '../templates/controllers/Templates.php'; require_once $template_controller_path; @@ -32,22 +52,30 @@ public function template(string $template_name, array $data): void { } $templates->$template_name($data); - } else { $template_controller_path = str_replace('../', APPPATH, $template_controller_path); - die('ERROR: Unable to find '.$template_name.' method in '.$template_controller_path.'.'); + die('ERROR: Unable to find ' . $template_name . ' method in ' . $template_controller_path . '.'); } } + /** + * Load a module's controller dynamically and instantiate it. + * + * @param class-string $target_module The name of the target module to load. + * + * @return void + * + * @throws ReflectionException If the target controller file cannot be found or the controller class cannot be instantiated. + */ public function module(string $target_module): void { $target_controller = ucfirst($target_module); - $target_controller_path = '../modules/'.$target_module.'/controllers/'.$target_controller.'.php'; + $target_controller_path = '../modules/' . $target_module . '/controllers/' . $target_controller . '.php'; if (!file_exists($target_controller_path)) { $child_module = $this->get_child_module($target_module); - $target_controller_path = '../modules/'.$target_module.'/'.$child_module.'/controllers/'.ucfirst($child_module).'.php'; - $ditch = '-'.$child_module.'/'.$child_module.'/controllers'; - $replace = '/'.$child_module.'/controllers'; + $target_controller_path = '../modules/' . $target_module . '/' . $child_module . '/controllers/' . ucfirst($child_module) . '.php'; + $ditch = '-' . $child_module . '/' . $child_module . '/controllers'; + $replace = '/' . $child_module . '/controllers'; $target_controller_path = str_replace($ditch, $replace, $target_controller_path); $target_module = $child_module; } @@ -56,25 +84,45 @@ public function module(string $target_module): void { $this->$target_module = new $target_module($target_module); } + /** + * Get the child module name from the target module name. + * + * @param string $target_module The name of the target module. + * + * @return string|null The name of the child module, or null if not found. + * + * @throws ReflectionException If the target module is not found. + */ private function get_child_module(string $target_module): string|null { $child_module_path = false; $bits = explode('-', $target_module); - if (count($bits)==2) { - if (strlen($bits[1])>0) { + if (count($bits) == 2) { + if (strlen($bits[1]) > 0) { $child_module = $bits[1]; } } if (!isset($child_module)) { http_response_code(404); - echo 'ERROR: Unable to locate '.$target_module.' module!'; + echo 'ERROR: Unable to locate ' . $target_module . ' module!'; die(); } return $child_module; } + /** + * Render a view. + * + * @param string $view The name of the view to render. + * @param array $data The data to pass to the view. + * @param bool $return_as_str Whether to return the view as a string or output it. + * + * @return string|null The rendered view as a string, or null if not rendered. + * + * @throws ReflectionException If a reflection error occurs while loading the view. + */ protected function view(string $view, array $data = [], bool $return_as_str = false): string|null { if ($this->parent_module !== '' && $this->child_module !== '') { // Load view from child module @@ -96,21 +144,45 @@ protected function view(string $view, array $data = [], bool $return_as_str = fa return $output; } } + return null; } - protected function load_view_file(string $view_path, array $data, bool $return_as_str): string|null { + /** + * Load a view file. + * + * @param string $view_path The path to the view file. + * @param array $data The data to pass to the view. + * @param bool $return_as_str Whether to return the view as a string or output it. + * + * @return string|null|false The rendered view as a string, or null if not rendered and $return_as_str is true, + * or false if not rendered and $return_as_str is false. + * + * @throws Exception If the view file does not exist. + */ + protected function load_view_file(string $view_path, array $data, bool $return_as_str): string|null|false { if (!file_exists($view_path)) { throw new Exception('View ' . $view_path . ' does not exist'); } if ($return_as_str) { ob_start(); require $view_path; - return ob_get_clean(); + return ob_get_clean() ?: null; } else { require $view_path; + return false; } } + /** + * Load a child view file. + * + * @param string $view The name of the view file. + * @param array $data The data to pass to the view. + * + * @return void + * + * @throws Exception If the view file does not exist. + */ private function load_child_view(string $view, array $data): void { extract($data); $view_path = APPPATH . 'modules/' . $this->parent_module . '/' . $this->child_module . '/views/' . $view . '.php'; @@ -125,14 +197,27 @@ private function load_child_view(string $view, array $data): void { } } - public function upload_picture(array $data): array|null { + /** + * Upload a picture file. + * + * @param array $data The data for the uploaded file. + * + * @return array|null The information of the uploaded file. + */ + public function upload_picture(array $data): ?array { $uploaded_file_info = $this->img_helper->upload($data); return $uploaded_file_info; } - public function upload_file(array $data): array|null { + /** + * Upload a file. + * + * @param array $data The data for the uploaded file. + * + * @return array|null The information of the uploaded file. + */ + public function upload_file(array $data): ?array { $uploaded_file_info = $this->file_helper->upload($data); return $uploaded_file_info; } - -} \ No newline at end of file +} From c664734e1af002aa8684b2d2fe78e04e168c000b Mon Sep 17 00:00:00 2001 From: Simon Field Date: Wed, 19 Apr 2023 16:45:38 +1000 Subject: [PATCH 086/100] Fixed some return types that were giving syntax errors Changed `string|null` to `?string` which is not a valid syntax in PHP Please note that this syntax is valid in PHP 7.1 and later versions. --- engine/Trongate.php | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/engine/Trongate.php b/engine/Trongate.php index 7a386614..a818cab5 100755 --- a/engine/Trongate.php +++ b/engine/Trongate.php @@ -93,7 +93,7 @@ public function module(string $target_module): void { * * @throws ReflectionException If the target module is not found. */ - private function get_child_module(string $target_module): string|null { + private function get_child_module(string $target_module): ?string { $child_module_path = false; $bits = explode('-', $target_module); @@ -123,7 +123,7 @@ private function get_child_module(string $target_module): string|null { * * @throws ReflectionException If a reflection error occurs while loading the view. */ - protected function view(string $view, array $data = [], bool $return_as_str = false): string|null { + protected function view(string $view, array $data = [], bool $return_as_str = false): ?string { if ($this->parent_module !== '' && $this->child_module !== '') { // Load view from child module $output = $this->load_view_file($view, $data, $return_as_str); @@ -148,18 +148,19 @@ protected function view(string $view, array $data = [], bool $return_as_str = fa } /** - * Load a view file. + * Load a view file and optionally return it as a string. * * @param string $view_path The path to the view file. * @param array $data The data to pass to the view. * @param bool $return_as_str Whether to return the view as a string or output it. * - * @return string|null|false The rendered view as a string, or null if not rendered and $return_as_str is true, - * or false if not rendered and $return_as_str is false. + * @return string|null The rendered view as a string, or null if not rendered + * and $return_as_str is true, or false if not rendered + * and $return_as_str is false. * * @throws Exception If the view file does not exist. */ - protected function load_view_file(string $view_path, array $data, bool $return_as_str): string|null|false { + protected function load_view_file(string $view_path, array $data, bool $return_as_str): ?string { if (!file_exists($view_path)) { throw new Exception('View ' . $view_path . ' does not exist'); } From 9d771a0a2984f8db3592f7cd10db24f56f952738 Mon Sep 17 00:00:00 2001 From: David Connelly Date: Wed, 26 Apr 2023 11:07:38 +0100 Subject: [PATCH 087/100] made top links on default admin template more meaningful --- public/themes/default_admin/black/admin.php | 14 +++++++------- public/themes/default_admin/blue/admin.php | 18 +++++++++--------- public/themes/default_admin/green/admin.php | 16 ++++++++-------- public/themes/default_admin/orange/admin.php | 16 ++++++++-------- public/themes/default_admin/purple/admin.php | 16 ++++++++-------- public/themes/default_admin/red/admin.php | 16 ++++++++-------- 6 files changed, 48 insertions(+), 48 deletions(-) diff --git a/public/themes/default_admin/black/admin.php b/public/themes/default_admin/black/admin.php index 145ef9a6..fddaea6c 100755 --- a/public/themes/default_admin/black/admin.php +++ b/public/themes/default_admin/black/admin.php @@ -15,13 +15,13 @@
    diff --git a/public/themes/default_admin/blue/admin.php b/public/themes/default_admin/blue/admin.php index 145ef9a6..3c8a7f61 100755 --- a/public/themes/default_admin/blue/admin.php +++ b/public/themes/default_admin/blue/admin.php @@ -15,18 +15,18 @@
- '); echo anchor('trongate_administrators/account', ''); echo anchor('trongate_administrators/logout', ''); diff --git a/public/themes/default_admin/green/admin.php b/public/themes/default_admin/green/admin.php index defca949..b9042ba0 100755 --- a/public/themes/default_admin/green/admin.php +++ b/public/themes/default_admin/green/admin.php @@ -15,14 +15,14 @@
diff --git a/public/themes/default_admin/orange/admin.php b/public/themes/default_admin/orange/admin.php index 145ef9a6..786eee1d 100755 --- a/public/themes/default_admin/orange/admin.php +++ b/public/themes/default_admin/orange/admin.php @@ -15,14 +15,14 @@
diff --git a/public/themes/default_admin/purple/admin.php b/public/themes/default_admin/purple/admin.php index 145ef9a6..29c767ef 100755 --- a/public/themes/default_admin/purple/admin.php +++ b/public/themes/default_admin/purple/admin.php @@ -15,14 +15,14 @@
diff --git a/public/themes/default_admin/red/admin.php b/public/themes/default_admin/red/admin.php index defca949..17fd2eaf 100755 --- a/public/themes/default_admin/red/admin.php +++ b/public/themes/default_admin/red/admin.php @@ -15,14 +15,14 @@
From 646104dd6d593115dbfa2962501fb063159c6b14 Mon Sep 17 00:00:00 2001 From: David Connelly Date: Wed, 26 Apr 2023 13:13:59 +0100 Subject: [PATCH 088/100] Fault Fix: extract() data function moved from ignition.php to Trongate.php so that data array is automatically extracted upon normal view file loading --- engine/Trongate.php | 1 + engine/ignition.php | 6 ------ 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/engine/Trongate.php b/engine/Trongate.php index a818cab5..18aac206 100755 --- a/engine/Trongate.php +++ b/engine/Trongate.php @@ -164,6 +164,7 @@ protected function load_view_file(string $view_path, array $data, bool $return_a if (!file_exists($view_path)) { throw new Exception('View ' . $view_path . ' does not exist'); } + extract($data); if ($return_as_str) { ob_start(); require $view_path; diff --git a/engine/ignition.php b/engine/ignition.php index ae99fba7..502c43a8 100644 --- a/engine/ignition.php +++ b/engine/ignition.php @@ -33,13 +33,7 @@ function load($template_file, $data=null) { } if (file_exists($file_path)) { - - if (isset($data)) { - extract($data); - } - require_once($file_path); - } else { die('
ERROR: View file does not exist at: '.$file_path); } From 2dd6f235228d42c5d28be232962580432254d02f Mon Sep 17 00:00:00 2001 From: David Connelly Date: Wed, 26 Apr 2023 13:24:29 +0100 Subject: [PATCH 089/100] Correction: reintroduced extract() to ignition.php. --- engine/ignition.php | 1 + 1 file changed, 1 insertion(+) diff --git a/engine/ignition.php b/engine/ignition.php index 502c43a8..75581e17 100644 --- a/engine/ignition.php +++ b/engine/ignition.php @@ -33,6 +33,7 @@ function load($template_file, $data=null) { } if (file_exists($file_path)) { + extract($data); require_once($file_path); } else { die('
ERROR: View file does not exist at: '.$file_path); From a8cbce16d25df9b0efcb25369d49744536a3b4a6 Mon Sep 17 00:00:00 2001 From: David Connelly Date: Wed, 26 Apr 2023 14:53:58 +0100 Subject: [PATCH 090/100] Correction, now extracting data array if isset --- engine/ignition.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/engine/ignition.php b/engine/ignition.php index 75581e17..cb9d54bc 100644 --- a/engine/ignition.php +++ b/engine/ignition.php @@ -33,7 +33,11 @@ function load($template_file, $data=null) { } if (file_exists($file_path)) { - extract($data); + + if (isset($data)) { + extract($data); + } + require_once($file_path); } else { die('
ERROR: View file does not exist at: '.$file_path); From 5e668a07364c101f2dde6f936e17ed2077084961 Mon Sep 17 00:00:00 2001 From: David Connelly Date: Thu, 27 Apr 2023 15:04:02 +0100 Subject: [PATCH 091/100] Correction to closeModal() - function now attempts to remove element only in instances where element exists on page. --- public/js/admin.js | 29 +++++++++++++++++------------ public/js/app.js | 29 +++++++++++++++++------------ 2 files changed, 34 insertions(+), 24 deletions(-) diff --git a/public/js/admin.js b/public/js/admin.js index 9cb39557..a93c46ed 100755 --- a/public/js/admin.js +++ b/public/js/admin.js @@ -60,18 +60,23 @@ function openModal(modalId) { } function closeModal() { - var modalContainer = _("modal-container"); - var openModal = modalContainer.firstChild; - - openModal.style.zIndex = -4; - openModal.style.opacity = 0; - openModal.style.marginTop = '12vh'; - openModal.style.display = 'none'; - body.appendChild(openModal); - - modalContainer.remove(); - var overlay = _("overlay"); - overlay.remove(); + var modalContainer = document.getElementById("modal-container"); + if (modalContainer) { + var openModal = modalContainer.firstChild; + + openModal.style.zIndex = -4; + openModal.style.opacity = 0; + openModal.style.marginTop = '12vh'; + openModal.style.display = 'none'; + document.body.appendChild(openModal); + + modalContainer.remove(); + + var overlay = document.getElementById("overlay"); + if (overlay) { + overlay.remove(); + } + } } function attemptEscCloseModal () { diff --git a/public/js/app.js b/public/js/app.js index 4a2e9151..d97b3313 100755 --- a/public/js/app.js +++ b/public/js/app.js @@ -70,18 +70,23 @@ function openModal(modalId) { } function closeModal() { - var modalContainer = _("modal-container"); - var openModal = modalContainer.firstChild; - - openModal.style.zIndex = -4; - openModal.style.opacity = 0; - openModal.style.marginTop = '12vh'; - openModal.style.display = 'none'; - body.appendChild(openModal); - - modalContainer.remove(); - var overlay = _("overlay"); - overlay.remove(); + var modalContainer = document.getElementById("modal-container"); + if (modalContainer) { + var openModal = modalContainer.firstChild; + + openModal.style.zIndex = -4; + openModal.style.opacity = 0; + openModal.style.marginTop = '12vh'; + openModal.style.display = 'none'; + document.body.appendChild(openModal); + + modalContainer.remove(); + + var overlay = document.getElementById("overlay"); + if (overlay) { + overlay.remove(); + } + } } function attemptEscCloseModal () { From 234a67721c4cc1cd2d96d844dc28da26df050b2d Mon Sep 17 00:00:00 2001 From: David Connelly Date: Fri, 28 Apr 2023 11:35:41 +0100 Subject: [PATCH 092/100] Correction to view method (thank you DaFa66!) --- engine/Trongate.php | 151 ++++++++++++++++++++++++++++++++------------ 1 file changed, 110 insertions(+), 41 deletions(-) diff --git a/engine/Trongate.php b/engine/Trongate.php index 18aac206..a8513c0d 100755 --- a/engine/Trongate.php +++ b/engine/Trongate.php @@ -112,69 +112,138 @@ private function get_child_module(string $target_module): ?string { return $child_module; } - /** - * Render a view. - * - * @param string $view The name of the view to render. - * @param array $data The data to pass to the view. - * @param bool $return_as_str Whether to return the view as a string or output it. - * - * @return string|null The rendered view as a string, or null if not rendered. - * - * @throws ReflectionException If a reflection error occurs while loading the view. - */ - protected function view(string $view, array $data = [], bool $return_as_str = false): ?string { - if ($this->parent_module !== '' && $this->child_module !== '') { - // Load view from child module - $output = $this->load_view_file($view, $data, $return_as_str); - if ($return_as_str) { + protected function view_old(string $view, array $data = [], ?bool $return_as_str = null): ?string { + + if ((isset($return_as_str)) || (gettype($data) == 'boolean')) { + $return_as_str = true; + } else { + $return_as_str = false; + } + + if (($this->parent_module !== '') && ($this->child_module !== '')) { + //load view from child module + if ($return_as_str == true) { + // Return output as string + ob_start(); + $this->load_child_view($view, $data); + $output = ob_get_clean(); return $output; + } else { + // Require child file + $this->load_child_view($view, $data); + return null; } } else { - // Normal view loading process - $module_name = $data['view_module'] ?? $this->module_name; + //normal view loading process + if (isset($data['view_module'])) { + $module_name = $data['view_module']; + } else { + $module_name = $this->module_name; + } + extract($data); + $view_path = APPPATH . 'modules/' . $module_name . '/views/' . $view . '.php'; - if (!file_exists($view_path)) { + + // Check for view file + if (file_exists($view_path)) { + + if ($return_as_str == true) { + // Return output as string + ob_start(); + require $view_path; + $output = ob_get_clean(); + return $output; + } else { + // Require view file + require $view_path; + return null; + } + } else { + // No view exists $view = str_replace('/', '/views/', $view); $view_path = APPPATH . 'modules/' . $view . '.php'; - } - $output = $this->load_view_file($view_path, $data, $return_as_str); - if ($return_as_str) { - return $output; + + if (file_exists($view_path)) { + + if ($return_as_str == true) { + // Return output as string + ob_start(); + require $view_path; + $output = ob_get_clean(); + return $output; + } else { + // Require view file + require $view_path; + return null; + } + } else { + throw new Exception('View ' . $view_path . ' does not exist'); + } } } - return null; } + //---------------- + /** - * Load a view file and optionally return it as a string. - * - * @param string $view_path The path to the view file. - * @param array $data The data to pass to the view. - * @param bool $return_as_str Whether to return the view as a string or output it. + * Renders a view and returns the output as a string, or to the browser. * - * @return string|null The rendered view as a string, or null if not rendered - * and $return_as_str is true, or false if not rendered - * and $return_as_str is false. - * - * @throws Exception If the view file does not exist. + * @param string $view The name of the view file to render. + * @param array $data An array of data to pass to the view file. + * @param bool|null $return_as_str If set to true, the output is returned as a string, otherwise to the browser. + * @return string|null If $return_as_str is true, returns the output as a string, otherwise returns null. */ - protected function load_view_file(string $view_path, array $data, bool $return_as_str): ?string { - if (!file_exists($view_path)) { - throw new Exception('View ' . $view_path . ' does not exist'); - } + protected function view(string $view, array $data = [], ?bool $return_as_str = null): ?string { + $return_as_str = $return_as_str ?? false; + + $view_path = $this->_get_view_path($view); extract($data); + if ($return_as_str) { + // Output as string ob_start(); require $view_path; - return ob_get_clean() ?: null; + $output = ob_get_clean(); + return $output; } else { + // Output view file require $view_path; - return false; + return null; + } + } + + /** + * Get the path of a view file. + * + * @param string $view The name of the view file. + * @param array $data The data to be passed to the view file. + * @return string The path of the view file. + * @throws Exception If the view file does not exist. + */ + function _get_view_path(string $view): string { + $module_name = $data['view_module'] ?? $this->module_name; + + if ($this->parent_module !== '' && $this->child_module !== '') { + // Load view from child module + $view_path = APPPATH . "modules/{$this->parent_module}/{$this->child_module}/views/{$view}.php"; + } else { + // Normal view loading process + $view_path = APPPATH . "modules/{$module_name}/views/{$view}.php"; + } + + if (file_exists($view_path)) { + return $view_path; + } else { + $error_message = $this->parent_module !== '' && $this->child_module !== '' ? + "View '{$view_path}' does not exist for child view" : + "View '{$view_path}' does not exist"; + throw new Exception($error_message); } } + //---------------- + /** * Load a child view file. * @@ -222,4 +291,4 @@ public function upload_file(array $data): ?array { $uploaded_file_info = $this->file_helper->upload($data); return $uploaded_file_info; } -} +} \ No newline at end of file From 074dca667202b323fdd7fe555837c26b5428ce47 Mon Sep 17 00:00:00 2001 From: David Connelly Date: Fri, 28 Apr 2023 11:41:01 +0100 Subject: [PATCH 093/100] Correction to view method and removed old view code (thank you DaFa66!) --- engine/Trongate.php | 74 --------------------------------------------- 1 file changed, 74 deletions(-) diff --git a/engine/Trongate.php b/engine/Trongate.php index a8513c0d..7a7fd0ce 100755 --- a/engine/Trongate.php +++ b/engine/Trongate.php @@ -112,80 +112,6 @@ private function get_child_module(string $target_module): ?string { return $child_module; } - protected function view_old(string $view, array $data = [], ?bool $return_as_str = null): ?string { - - if ((isset($return_as_str)) || (gettype($data) == 'boolean')) { - $return_as_str = true; - } else { - $return_as_str = false; - } - - if (($this->parent_module !== '') && ($this->child_module !== '')) { - //load view from child module - if ($return_as_str == true) { - // Return output as string - ob_start(); - $this->load_child_view($view, $data); - $output = ob_get_clean(); - return $output; - } else { - // Require child file - $this->load_child_view($view, $data); - return null; - } - } else { - //normal view loading process - if (isset($data['view_module'])) { - $module_name = $data['view_module']; - } else { - $module_name = $this->module_name; - } - - extract($data); - - $view_path = APPPATH . 'modules/' . $module_name . '/views/' . $view . '.php'; - - // Check for view file - if (file_exists($view_path)) { - - if ($return_as_str == true) { - // Return output as string - ob_start(); - require $view_path; - $output = ob_get_clean(); - return $output; - } else { - // Require view file - require $view_path; - return null; - } - } else { - // No view exists - $view = str_replace('/', '/views/', $view); - $view_path = APPPATH . 'modules/' . $view . '.php'; - - if (file_exists($view_path)) { - - if ($return_as_str == true) { - // Return output as string - ob_start(); - require $view_path; - $output = ob_get_clean(); - return $output; - } else { - // Require view file - require $view_path; - return null; - } - } else { - throw new Exception('View ' . $view_path . ' does not exist'); - } - } - } - } - - //---------------- - /** * Renders a view and returns the output as a string, or to the browser. * From c710a1b61eb69e258c80d9b83f932dd1d77db819 Mon Sep 17 00:00:00 2001 From: David Connelly Date: Fri, 28 Apr 2023 19:06:46 +0100 Subject: [PATCH 094/100] Removed several weak warnings. --- .gitignore | 2 + engine/Trongate.php | 99 ++++++++++++++++----------------------------- 2 files changed, 37 insertions(+), 64 deletions(-) diff --git a/.gitignore b/.gitignore index 4df66154..deef7f17 100755 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ \.phpintel/ +.idea/ +.vscode/ diff --git a/engine/Trongate.php b/engine/Trongate.php index 7a7fd0ce..561ab3ff 100755 --- a/engine/Trongate.php +++ b/engine/Trongate.php @@ -84,18 +84,15 @@ public function module(string $target_module): void { $this->$target_module = new $target_module($target_module); } - /** - * Get the child module name from the target module name. - * - * @param string $target_module The name of the target module. - * - * @return string|null The name of the child module, or null if not found. - * - * @throws ReflectionException If the target module is not found. - */ + /** + * Get the child module name from the target module name. + * + * @param string $target_module The name of the target module. + * + * @return string|null The name of the child module, or null if not found. + */ private function get_child_module(string $target_module): ?string { - $child_module_path = false; - $bits = explode('-', $target_module); + $bits = explode('-', $target_module); if (count($bits) == 2) { if (strlen($bits[1]) > 0) { @@ -112,14 +109,16 @@ private function get_child_module(string $target_module): ?string { return $child_module; } - /** - * Renders a view and returns the output as a string, or to the browser. - * - * @param string $view The name of the view file to render. - * @param array $data An array of data to pass to the view file. - * @param bool|null $return_as_str If set to true, the output is returned as a string, otherwise to the browser. - * @return string|null If $return_as_str is true, returns the output as a string, otherwise returns null. - */ + /** + * Renders a view and returns the output as a string, or to the browser. + * + * @param string $view The name of the view file to render. + * @param array $data An array of data to pass to the view file. + * @param bool|null $return_as_str If set to true, the output is returned as a string, otherwise to the browser. + * + * @return string|null If $return_as_str is true, returns the output as a string, otherwise returns null. + * @throws \Exception + */ protected function view(string $view, array $data = [], ?bool $return_as_str = null): ?string { $return_as_str = $return_as_str ?? false; @@ -130,8 +129,8 @@ protected function view(string $view, array $data = [], ?bool $return_as_str = n // Output as string ob_start(); require $view_path; - $output = ob_get_clean(); - return $output; + + return ob_get_clean(); } else { // Output view file require $view_path; @@ -139,62 +138,36 @@ protected function view(string $view, array $data = [], ?bool $return_as_str = n } } - /** - * Get the path of a view file. - * - * @param string $view The name of the view file. - * @param array $data The data to be passed to the view file. - * @return string The path of the view file. - * @throws Exception If the view file does not exist. - */ + /** + * Get the path of a view file. + * + * @param string $view The name of the view file. + * + * @return string The path of the view file. + * @throws \Exception If the view file does not exist. + */ function _get_view_path(string $view): string { $module_name = $data['view_module'] ?? $this->module_name; if ($this->parent_module !== '' && $this->child_module !== '') { // Load view from child module - $view_path = APPPATH . "modules/{$this->parent_module}/{$this->child_module}/views/{$view}.php"; + $view_path = APPPATH . "modules/$this->parent_module/$this->child_module/views/$view.php"; } else { // Normal view loading process - $view_path = APPPATH . "modules/{$module_name}/views/{$view}.php"; + $view_path = APPPATH . "modules/$module_name/views/$view.php"; } if (file_exists($view_path)) { return $view_path; } else { $error_message = $this->parent_module !== '' && $this->child_module !== '' ? - "View '{$view_path}' does not exist for child view" : - "View '{$view_path}' does not exist"; + "View '$view_path' does not exist for child view" : + "View '$view_path' does not exist"; throw new Exception($error_message); } } - //---------------- - - /** - * Load a child view file. - * - * @param string $view The name of the view file. - * @param array $data The data to pass to the view. - * - * @return void - * - * @throws Exception If the view file does not exist. - */ - private function load_child_view(string $view, array $data): void { - extract($data); - $view_path = APPPATH . 'modules/' . $this->parent_module . '/' . $this->child_module . '/views/' . $view . '.php'; - - // Check for view file - if (file_exists($view_path)) { - // Require view file - require_once $view_path; - } else { - // No view exists - throw new Exception('view ' . $view_path . ' does not exist'); - } - } - - /** + /** * Upload a picture file. * * @param array $data The data for the uploaded file. @@ -202,8 +175,7 @@ private function load_child_view(string $view, array $data): void { * @return array|null The information of the uploaded file. */ public function upload_picture(array $data): ?array { - $uploaded_file_info = $this->img_helper->upload($data); - return $uploaded_file_info; + return $this->img_helper->upload($data); } /** @@ -214,7 +186,6 @@ public function upload_picture(array $data): ?array { * @return array|null The information of the uploaded file. */ public function upload_file(array $data): ?array { - $uploaded_file_info = $this->file_helper->upload($data); - return $uploaded_file_info; + return $this->file_helper->upload($data); } } \ No newline at end of file From b731fead64bfd1a99cf0fe5cc146e865671993c7 Mon Sep 17 00:00:00 2001 From: Simon Field Date: Sat, 29 Apr 2023 16:46:23 +1000 Subject: [PATCH 095/100] Reinstate $data['view_module'] You can now change the calling module name --- engine/Trongate.php | 75 ++++++++++++++++++++++++--------------------- 1 file changed, 40 insertions(+), 35 deletions(-) diff --git a/engine/Trongate.php b/engine/Trongate.php index 561ab3ff..737a62e4 100755 --- a/engine/Trongate.php +++ b/engine/Trongate.php @@ -9,6 +9,11 @@ class Trongate { protected string $parent_module = ''; protected string $child_module = ''; + /** + * Constructor for Trongate class. + * + * @param string|null $module_name The name of the module to use, or null for default module. + */ public function __construct(?string $module_name = null) { $this->module_name = $module_name; $this->modules = new Modules; @@ -84,15 +89,15 @@ public function module(string $target_module): void { $this->$target_module = new $target_module($target_module); } - /** - * Get the child module name from the target module name. - * - * @param string $target_module The name of the target module. - * - * @return string|null The name of the child module, or null if not found. - */ + /** + * Get the child module name from the target module name. + * + * @param string $target_module The name of the target module. + * + * @return string|null The name of the child module, or null if not found. + */ private function get_child_module(string $target_module): ?string { - $bits = explode('-', $target_module); + $bits = explode('-', $target_module); if (count($bits) == 2) { if (strlen($bits[1]) > 0) { @@ -109,28 +114,28 @@ private function get_child_module(string $target_module): ?string { return $child_module; } - /** - * Renders a view and returns the output as a string, or to the browser. - * - * @param string $view The name of the view file to render. - * @param array $data An array of data to pass to the view file. - * @param bool|null $return_as_str If set to true, the output is returned as a string, otherwise to the browser. - * - * @return string|null If $return_as_str is true, returns the output as a string, otherwise returns null. - * @throws \Exception - */ + /** + * Renders a view and returns the output as a string, or to the browser. + * + * @param string $view The name of the view file to render. + * @param array $data An array of data to pass to the view file. + * @param bool|null $return_as_str If set to true, the output is returned as a string, otherwise to the browser. + * + * @return string|null If $return_as_str is true, returns the output as a string, otherwise returns null. + * @throws \Exception + */ protected function view(string $view, array $data = [], ?bool $return_as_str = null): ?string { $return_as_str = $return_as_str ?? false; + $module_name = $data['view_module'] ?? $this->module_name; - $view_path = $this->_get_view_path($view); + $view_path = $this->_get_view_path($view, $module_name); extract($data); if ($return_as_str) { // Output as string ob_start(); require $view_path; - - return ob_get_clean(); + return ob_get_clean(); } else { // Output view file require $view_path; @@ -138,16 +143,16 @@ protected function view(string $view, array $data = [], ?bool $return_as_str = n } } - /** - * Get the path of a view file. - * - * @param string $view The name of the view file. - * - * @return string The path of the view file. - * @throws \Exception If the view file does not exist. - */ - function _get_view_path(string $view): string { - $module_name = $data['view_module'] ?? $this->module_name; + /** + * Get the path of a view file. + * + * @param string $view The name of the view file. + * @param string $module_name Module name to which the view belongs. + * + * @return string The path of the view file. + * @throws \Exception If the view file does not exist. + */ + function _get_view_path(string $view, ?string $module_name): string { if ($this->parent_module !== '' && $this->child_module !== '') { // Load view from child module @@ -167,7 +172,7 @@ function _get_view_path(string $view): string { } } - /** + /** * Upload a picture file. * * @param array $data The data for the uploaded file. @@ -175,7 +180,7 @@ function _get_view_path(string $view): string { * @return array|null The information of the uploaded file. */ public function upload_picture(array $data): ?array { - return $this->img_helper->upload($data); + return $this->img_helper->upload($data); } /** @@ -186,6 +191,6 @@ public function upload_picture(array $data): ?array { * @return array|null The information of the uploaded file. */ public function upload_file(array $data): ?array { - return $this->file_helper->upload($data); + return $this->file_helper->upload($data); } -} \ No newline at end of file +} From 041df710ab1c4914dc83b4845f2ab9ca1ab5f902 Mon Sep 17 00:00:00 2001 From: David Connelly Date: Sat, 29 Apr 2023 13:56:36 +0100 Subject: [PATCH 096/100] Correction to api_auth() method --- engine/tg_helpers/url_helper.php | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/engine/tg_helpers/url_helper.php b/engine/tg_helpers/url_helper.php index 76d5f413..3dc8bf3e 100644 --- a/engine/tg_helpers/url_helper.php +++ b/engine/tg_helpers/url_helper.php @@ -126,7 +126,6 @@ function api_auth() { $filepath = APPPATH . 'modules/' . $current_module . '/assets/api.json'; if (file_exists($filepath)) { - //extract the rules for the current path $target_method = $segments[1]; $settings = file_get_contents($filepath); @@ -137,34 +136,30 @@ function api_auth() { foreach ($endpoints as $rule_name => $api_rule_value) { - $segments_match = true; - if (isset($api_rule_value['url_segments'])) { - //ignore placeholders for decent comparison + //make sure the current URL segments match against the required segments $target_url_segments = $api_rule_value['url_segments']; $bits = explode('/', $target_url_segments); - $required_bits = []; + $required_segments = []; foreach ($bits as $key => $value) { - if (!is_numeric(strpos($value, '{'))) { $required_segments[$key] = $value; } } - foreach ($current_uri_bits as $key => $value) { + $num_required_segments = count($required_segments); + foreach ($current_uri_bits as $key => $value) { if (isset($required_segments[$key])) { - - if ($value !== $required_segments[$key]) { - $segments_match = false; + if ($value === $required_segments[$key]) { + $num_required_segments--; } } } - if ($segments_match === true) { - + if ($num_required_segments === 0) { $token_validation_data['endpoint'] = $rule_name; $token_validation_data['module_name'] = $current_module; $token_validation_data['module_endpoints'] = $endpoints; From ea22f4b86fb3daa9b58e5136e58a64a8feabacba Mon Sep 17 00:00:00 2001 From: David Connelly Date: Sat, 29 Apr 2023 14:03:41 +0100 Subject: [PATCH 097/100] Changed engine number to 1.3.3045 --- engine/license.txt | 2 +- license.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/engine/license.txt b/engine/license.txt index 1e76ce3b..7ad060f2 100755 --- a/engine/license.txt +++ b/engine/license.txt @@ -3,7 +3,7 @@ * * An open source PHP framework for web developers who like to break the rules * - * Version: 1.3.3044 + * Version: 1.3.3045 * * This product is released under the MIT License (MIT) * diff --git a/license.txt b/license.txt index 1e76ce3b..7ad060f2 100755 --- a/license.txt +++ b/license.txt @@ -3,7 +3,7 @@ * * An open source PHP framework for web developers who like to break the rules * - * Version: 1.3.3044 + * Version: 1.3.3045 * * This product is released under the MIT License (MIT) * From 0109601de9d14e61742c8fd2762f01d85571d606 Mon Sep 17 00:00:00 2001 From: David Connelly Date: Sat, 29 Apr 2023 14:31:09 +0100 Subject: [PATCH 098/100] Correction: tokens should only be collected from header - not from cookies or session vars --- engine/Standard_endpoints.php | 5 ----- 1 file changed, 5 deletions(-) diff --git a/engine/Standard_endpoints.php b/engine/Standard_endpoints.php index df917b1b..e3b145e2 100644 --- a/engine/Standard_endpoints.php +++ b/engine/Standard_endpoints.php @@ -795,11 +795,6 @@ public function make_sure_allowed($target_endpoint, $table_name) { $this->module('trongate_tokens'); $token = (isset($_SERVER['HTTP_TRONGATETOKEN']) ? $_SERVER['HTTP_TRONGATETOKEN'] : false); - if ((($token === '') || ($token === false)) && (segment(1) !== 'api')) { - //attempt to fetch a token from cookie or session data - $token = $this->trongate_tokens->_attempt_get_valid_token(); - } - $endpoint_auth_rules = $target_endpoint['authorization']; $var_type = gettype($endpoint_auth_rules); From 137f02222711a8dd029d6ae95d1dd54846e4525a Mon Sep 17 00:00:00 2001 From: David Connelly Date: Sat, 29 Apr 2023 14:41:37 +0100 Subject: [PATCH 099/100] adjustment to CSS so that only the first label on the page gets a top margin of 3em --- engine/views/api_explorer_style.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/engine/views/api_explorer_style.php b/engine/views/api_explorer_style.php index 85a6b1c9..82b0cf9e 100644 --- a/engine/views/api_explorer_style.php +++ b/engine/views/api_explorer_style.php @@ -280,9 +280,12 @@ font-size: .9em; } +#test-endpoint-modal > div.modal-body > form > label:nth-child(1) { + margin-top: 3em; +} + label { font-weight: bold; - margin-top: 3em; } textarea { From 09e2a62466127af59b4d881655ef566184c17f65 Mon Sep 17 00:00:00 2001 From: Simon Field Date: Tue, 2 May 2023 22:25:03 +1000 Subject: [PATCH 100/100] Fixed error being thrown when using normal view from a sub module Change protected ?string $module_name; to protected ?string $module_name = ''; Which fixes an error when parent::__construct(); not included in __construct() Uncaught Error: Typed property Trongate::$module_name must not be accessed before initialization add links to the Trongate docs for view() and template() methods --- engine/Trongate.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/engine/Trongate.php b/engine/Trongate.php index 737a62e4..7ba115e4 100755 --- a/engine/Trongate.php +++ b/engine/Trongate.php @@ -5,7 +5,7 @@ class Trongate { protected Modules $modules; private ?Model $model; - protected ?string $module_name; + protected ?string $module_name = ''; protected string $parent_module = ''; protected string $child_module = ''; @@ -43,6 +43,8 @@ public function load(string $helper): void { * @return void * * @throws Exception If the template controller file cannot be found or the template method does not exist. + * + * @see https://trongate.io/docs/information/what-are-templates */ public function template(string $template_name, array $data = []): void { $template_controller_path = '../templates/controllers/Templates.php'; @@ -123,6 +125,8 @@ private function get_child_module(string $target_module): ?string { * * @return string|null If $return_as_str is true, returns the output as a string, otherwise returns null. * @throws \Exception + * + * @see https://trongate.io/docs/information/understanding-view-files */ protected function view(string $view, array $data = [], ?bool $return_as_str = null): ?string { $return_as_str = $return_as_str ?? false;