Jump to content

mac_gyver

Staff Alumni
  • Posts

    5,535
  • Joined

  • Days Won

    191

mac_gyver last won the day on July 18

mac_gyver had the most liked content!

4 Followers

About mac_gyver

Profile Information

  • Gender
    Not Telling

Recent Profile Visitors

158,481 profile views

mac_gyver's Achievements

Prolific Member

Prolific Member (5/5)

668

Reputation

157

Community Answers

  1. here are some implementation practices - the form processing code and form should be on the same page. by putting them on separate pages, you are creating a lot of extra code. by only validating one input at a time and not having the form fields 'sticky', you are providing a poor User eXperience (UX). by storing the 'login_attempts' and 'lockout_time' in session variables, a nefarious user/bot can get unlimited new login attempts by simply not propagating the session id cookie between requests. you must store this data persistently on the server in a database table. the only user related value you should store in a session variable upon successful login is the user id (autoincrement primary index.) you should query on each page request to get any other user data, so that any changes made to the user data will take effect on the very next page request, without requiring the user to log out and back in again. the way a 'remember me' operation should be implemented is that if the remember me checkbox is checked, at the point of successfully verifying the user's credentials, generate a unique token, store that in a cookie and in a database 'remember me' table that also includes the user id, and the current datatime, for a determining token expiration. on any page request, if the remember me token cookie is set, query to find a matching row in the remember me table. if there is a row and the token is not timed out, use the user id from that row to set the session variable that identifies who the logged in user is. the rest of the code then uses this value in the session variable, just like it was set in the login form processing code. the registration process, unless being performed by an administrator, which your code is not doing, should not include the role. the role should not be something that the user can decide when they register. modern php (8+) uses exceptions for database statement errors by default - connection, query, prepare, and execute. any discrete logic you currently have testing the result of these statements should be removed since it will never get executed upon an error. both the username and email must be unique or you should only use the email and forget about a separate username. the correct way of determining if a unique value already exists in a database table is to define the column(s) as a unique index, just attempt to insert the data, and detect in the exception catch logic for the insert query if a duplicate index error (number) occurred. any form processing code should keep for the form data as a set, in an array variable, then operate on elements in this array variable throughout the rest of the code. i.e. don't write out a line of code copying every $_POST variable to a discrete variable. you need to trim ALL the user supplied inputs, mainly so that you can detect if all white-space characters were entered, before validating the data. you need to use an array to hold user/validation errors, and validate all the inputs at once, storing the errors in the array using the field name as the array index. after the end of the validation logic, if there are no errors (the array will be empty), use the submitted form data. in the login validation logic, all you really care about is that the required inputs are are not empty strings, after being trimmed. by providing additional feedback to a nefarious user/bot, you are helping narrow down the values they need to try.
  2. the above line is missing any { }, so the only line of code that gets executed for an is_dir() is the - echo '<strong>'.$directory .'</strong> <br>'; all the rest of the lines get executed regardless of what $directory is. i recommend that you always format your code so that you can see when it is actually doing.
  3. if you use a cookie or the session to hold this data, it can be bypassed by simply deleting the cookie or not propagating the cookie or session id cookie between requests. you must store this data persistently on the server, in a database table. next, you are not trying to lock the account, you are preventing login attempts for an account, from a device (client type) and its location (ip). if you actually lock the account, it will allow someone to log out and lock out a legitimate user, by just making a bunch of bad login attempts for an account. once you have stored the data in a database table, on each login attempt, you would query to find if, how many, and how long ago the bad login attempts were for the account, for the device (client type) and its location (ip). If the current time is greater than the time limit you have chosen from the last bad attempt, you would process the login attempt.
  4. have you examined what the $_GET data is? echo '<pre>'; print_r($_GET); echo '</pre>';
  5. according to the 1st parameter, you are making a GET request. not a POST request. the main point of my 1st reply in this thread is that your code must validate (all) input data before using it. the account_no input is required (not an empty string), it must consist of a specific set of characters (decimal digits), and it might need to be a specific length or range of lengths. if it is not valid, it is an application error and you should setup and output a useful error message for each of these possible validation steps, and not attempt to use the input data. your first posted code and the code using the null coalescing operator are suppressing any helpful php errors (you would be getting an undefined index error) that would be pointing out that there is no expected entry in $_POST. so, you must change the php code to use $_GET['account_no'], and you must trim, then validate the input before using it. you would only get to the point of testing if the value is in the array of $valid_accounts if the input is valid.
  6. the reason for unusual operation is the ternary operator without a middle term, that the input is probably not what you expect, and php's type casting. when you leave out the middle term in the ternary operator, when the first term evaluates to true, the value used is whatever the first term is, which will be a boolean true due to the empty() statement. instead, your post method form processing code should - detect if a post method form was submitted before referencing any of the form data. detect if there is $_POST data (in case the post_max_size setting has been exceeded.) keep the form data as a set in a php array variable, then operate on elements in this array variable throughout the rest of the code. trim all the input data, mainly so that you can detect if all white-space characters were entered. validate all inputs, storing user/validation errors in an array using the field name as the array index. after the end of the validation logic, if there are no user/validation errors, use the form data. after using the form data, if there are no user/validation errors, perform a redirect to the exact same url of the current page to cause a get request for that page. this will prevent the browser from trying to resubmit the form data should that page get browsed back to or reloaded. if you want to display a one-time success message, store it or a flag value in a session variable, then test for, display the success message, and clear the session variable at the appropriate location in the html document. if there are user/validation errors, the code will continue on to display the html document, where you will test for and display any errors, redisplay the form, populating fields with existing data, so that the user only needs to correct the invalid input(s) and can resubmit the form.
  7. i recommend that you validate the resulting web page at validator.w3.org
  8. i don't know what this will look like incorporated into the yii syntax, but this is what i used with just jquery/javascript - // the element that was clicked on var e = $(this); $.post("index.php?r=attendancereport/standard&aid="+$("#academicyearid").val()+ "&ascid="+$("#ascid").val()+"&sid="+e.val(),function(data){ e.closest('tr').find('.s').html(data); });
  9. that's because the current code is looping over the collection of class = s elements, and setting them all to the data that is returned - $(".s").each(function(k,v) { $(".s").html(data); //Populates the Standard drop-down having class .s with the data from the Standard function } ); you need to 'find' the correct class = s element and set it to the data that is returned. take a look at the jquery .parent() method to find a common parent container that all the related form fields are in, then use the jquery .find() method to find the element with class = s.
  10. the most immediate problem is that the onChange code for the Student select/option menu needs to get the correct sid value. by using $(".i") you are getting a collection of all the class = i elements, not the current one. using $(this) should work (all suggestions are untested unless indicated otherwise.) next, requiring the user to select each student from a select/option menu is a poor User Interface (UI) and provides a bad User eXperience (UX). you should simply list out each Student, the Standard select/option menu with any existing choice pre-selected, and the Attendance select/option menu with the Present choice pre-selected (the most common case.) the user should only need to make the fewest number of changes to the choices and submit the form, so, only make any Standard changes and select which students are Absent.
  11. here's a tested example showing the suggested method - <?php // fake a request start and end date - 1st May 2025 to 8th May 2025 $start_date = "2025-05-01"; $end_date = "2025-05-08"; // exclude check-out date from length of stay/pricing $end = new DateTime($end_date); $end = $end->modify( '-1 day' ); $end_date = $end->format("Y-m-d"); // define default weekday and weekend prices $default_price = []; $default_price['wd'] = (object)['name'=>'Standard Weekday','price'=>'60']; $default_price['we'] = (object)['name'=>'Standard Weekend','price'=>'80']; // define seasonal prices $seasonPrices = []; $seasonPrices[] = (object)["SeasonName"=>"Spring half-term", "SeasonStart"=>'2025-02-21', "SeasonEnd"=>'2025-03-03', "SeasonPrice"=>'100']; $seasonPrices[] = (object)["SeasonName"=>"Easter", "SeasonStart"=>'2025-04-11', "SeasonEnd"=>'2025-04-27', "SeasonPrice"=>'100']; $seasonPrices[] = (object)["SeasonName"=>"May Day", "SeasonStart"=>'2025-05-02', "SeasonEnd"=>'2025-05-05', "SeasonPrice"=>'110']; $seasonPrices[] = (object)["SeasonName"=>"Summer half-term", "SeasonStart"=>'2025-05-23', "SeasonEnd"=>'2025-06-01', "SeasonPrice"=>'110']; $seasonPrices[] = (object)["SeasonName"=>"Summer", "SeasonStart"=>'2025-07-18', "SeasonEnd"=>'2025-09-01', "SeasonPrice"=>'120']; $seasonPrices[] = (object)["SeasonName"=>"Autumn half-term", "SeasonStart"=>'2025-10-24', "SeasonEnd"=>'2025-11-02', "SeasonPrice"=>'110']; $seasonPrices[] = (object)["SeasonName"=>"Christmas", "SeasonStart"=>'2025-12-19', "SeasonEnd"=>'2026-01-04', "SeasonPrice"=>'120']; // start by creating an array of request dates filled with the default weekday price $request = array_fill_keys(Cal::bookedArray($start_date, $end_date), $default_price['wd']->price); // get the weekend prices between the request start and end dates $weekend_prices = get_we_prices($start_date,$end_date,$default_price); // put the weekend prices into the array $request = array_replace($request,$weekend_prices); // get the seasonal prices between the request start and end dates $season_prices = get_prices($start_date,$end_date,$seasonPrices); // put the seasonal prices into the array $request = array_replace($request,$season_prices); // examine the result echo '<pre>'; print_r($request); echo '</pre>'; echo "Total: " . array_sum($request); // get the weekend prices between the request start and end dates function get_we_prices($start_date,$end_date,$default_price) { $dates = []; // find 1st Sat of the start date $date = date('Y-m-d',strtotime("first sat of $start_date")); // generate all the Saturdays between the start and end dates $start = new DateTime($date); $end = new DateTime($end_date); $end = $end->modify( '+1 day' ); // include the end point $interval = new DateInterval('P7D'); // 7 days $daterange = new DatePeriod($start, $interval ,$end); foreach($daterange as $date){ $dates[$date->format("Y-m-d")] = $default_price['we']->price; } // same as above but for Sundays // find 1st Sun of the start date $date = date('Y-m-d',strtotime("first sun of $start_date")); // generate all the Sundays between the start and end dates $start = new DateTime($date); $end = new DateTime($end_date); $end = $end->modify( '+1 day' ); // include the end point $interval = new DateInterval('P7D'); // 7 days $daterange = new DatePeriod($start, $interval ,$end); foreach($daterange as $date){ $dates[$date->format("Y-m-d")] = $default_price['we']->price; } return $dates; } // get the seasonal prices between the request start and end dates function get_prices($start,$end,$seasonPrices) { $result = []; foreach($seasonPrices as $row) { // keep entries that match the requested start/end // SeasonEnd >= start AND end >= SeasonStart if($row->SeasonEnd >= $start && $end >= $row->SeasonStart) { // expand this entry and only keep the values between the start and end (for the case of spanning the start or end date) $entry = Cal::bookedArray($row->SeasonStart, $row->SeasonEnd); foreach($entry as $date) { // if date between start and end, keep it if($start <= $date && $date <= $end) { $result[$date] = $row->SeasonPrice; } } } } return $result; } class cal { // produce a date range from start to end public static function bookedArray($start,$end) { $start = new DateTime($start); $end = new DateTime($end); $end = $end->modify( '+1 day' ); // include the end point $interval = new DateInterval('P1D'); // 1 day $daterange = new DatePeriod($start, $interval ,$end); $dates = array(); foreach($daterange as $date){ $dates[] = $date->format("Y-m-d"); } return $dates; } }
  12. you are constantly changing data types, names, and adding features. this makes writing code extremely wasteful. you need to define everything possible, before you write any code. you also need to define what inputs you have for any operation, what processing you are going to do based on those inputs, and what result or output you are going to produce. as to a simple, straightforward solution, i recommend that you read the suggestions in the previous thread, about building an array with the dates as the array index and the values being whatever you are trying to produce. in that thread, the array was for the special events for the calendar being displayed. in this thread, the array is for the requested date range and the price for each day in that range. the inputs to this code are the start and end dates of the request, the standard week day and week end prices, and the seasonal pricing. you would start by creating an array using the request range of dates as the array index. you would fill in the array with the week day and week end prices. you would then replace any price that matches the seasonal pricing data. when you are done, you will have an array with all the days of the request as the index, and the price for each day. you can then just sum the prices to get the total. here's a procedural function that gets the seasonal prices, given the request start and end date - // get seasonal prices between start and end date function get_prices($start,$end,$seasonPrices) { $result = []; foreach($seasonPrices as $row) { // keep entries that match the requested start/end // SeasonEnd >= start AND end >= SeasonStart if($row->SeasonEnd >= $start && $end >= $row->SeasonStart) { // expand this entry and only keep the values between the start and end (for the case of spanning the start or end date) $entry = Cal::bookedArray($row->SeasonStart, $row->SeasonEnd); foreach($entry as $date) { // if date between start and end, keep it if($start <= $date && $date <= $end) { $result[$date] = $row->SeasonPrice; } } } } return $result; } you can use array_replace() with the array you are building and the array returned by the above function to replace the standard prices in that array with the seasonal prices.
  13. for the posted information, the requested date range of 2025-05-06 to 2025-05-22 doesn't match any of the $seasonPrices data. It starts one day after the end of the May Day range and ends one day before the start of the Summer half-term range. it should use the base/default price for every day. since you are using a standard date format, you can directly perform date comparisons by order, as mentioned in the previous thread. you can directly compare a date to the $seasonPrices SeasonStart and SeasonEnd values to find if it is between a date range. i would write a function/class-method that accepts a date input, loops over the $seasonPrices data, returns the first price that is between the SeasonStart and SeasonEnd values, or returns zero (or some other easily detected value) if it reaches the end without finding a match. as a procedural function, something like - function get_price($date,$seasonPrices) { foreach($seasonPrices as $row) { // date between start and end if($row->SeasonStart <= $date && $date <= $row->SeasonEnd) { return $row->SeasonPrice; } } // no match return 0; }
  14. what exactly is the single $booked data for vs the array of $bookings? i have the following recommendations - use a standard yyyy-mm-dd date format. if/when you store the data in a database, this will be necessary to allow queries to compare dates by order. it will also make your current code easier to understand. the bookings data should be stored in a database table. you should have a start_date and an end_date input to the code on this page, so that the code will work for any date range, from multiple years down to a single month. you are looping over the $res array for every date being displayed, currently for the whole year. i recommend that you instead produce an array using the date as the array index, and the stored value indicating what is occurring on that date, limited to be between the start_date and end_date mentioned above. as you are producing the output, you can simply test, no loop required, if there is an entry in this array, using isset(), then get the stored value if there is one, and use it to control what you output for the date. with a little conditional logic, you don't need separate logic to deal with the first (partial) week, then repeat that logic for the remainder of each month. if you use php's short-open-print tag and leave out the ; right before a closing tag, you can use simple syntax like - <?=$some_var?> to output values in the markup. use php's DateInterval and DatePeriod to expand the booking date ranges. here's example code for item #7 - $start = new DateTime($start); $end = new DateTime($end); $end = $end->modify('+1 day'); // include the end point $interval = new DateInterval('P1D'); // 1 day $daterange = new DatePeriod($start, $interval ,$end); $dates = array(); foreach($daterange as $date){ $dates[] = $date->format("Y-m-d"); }
  15. this is a 'contact us' form. for the client to be able to receive and read the emails, they must be sent to an email address that the client has access to. is this To: address at their web hosting (mail_box_name@their_doman_name) or somewhere else, such as a gmail address ([email protected])? i didn't find any information about using unauthenticated smtp at hostgator, but if this is possible, it would require an email account to be created within cpanel for the domain name that's hosted at hostgator, then this email address would be used as the From: address. the To: address can then be anywhere, even the same as the From: address. everything I saw about hostgator email used smtp authentication, which needs an email account to be created within cpanel for the domain name hosted at hostgator, with the password known. if they have an email address at their domain name, but the password is not known, it needs to be reset to a known value within cpanel. for php to be able to send to (the From: and To: addresses would be the same and be the email account at hostgator) or through (the From: address would be the email account at hostgator and the To: can be anywhere) the mail server at hostgator, you would use the hostgator server settings. see the cPanel Emails, Outgoing Email Settings at this link - https://www.hostgator.com/help/article/email-connection-settings#cpanelemails you can, using the phpmailer/swiftmailer class, send an email to or through an external mail server, such as gmail, but this requires the username/password of the account on that external mail server.
×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.