import { addBanner, addArticle, addHeader, addParagraph, addSubHeader } from '/scripts/article.js';
import { addInset, addInsetList, addInsetCodeListing, addInsetBulletList } from '/scripts/inset.js';
import { addImageWithCaption, addButtonGroup } from '/scripts/visuals.js';
import { menu } from '/scripts/web_dev_buttons.js';
const main = document.querySelector("main");
main.append(addBanner("Getting Started as a Full Stack Web Developer", "Tom Geller", "Course Project: Hotel Reservations, Part 2"))
main.append(addArticle())
const article = document.querySelector("article")
article.append(addHeader("Break the Site into Modular Pieces"))
article.append(addParagraph("Before we do any more with the project site, we want to think a little bit about how our website is organised. At the moment, we only have one page and all of our HTML for the site is in that page. We may want to add additional pages to the site and that gives us a little bit of a problem in that there will be some aspects of the page that we want to be common to most or all of our pages such as the header and footer."))
article.append(addParagraph("We can copy the code from one page to another but that is cumbersome and difficult to maintain. If a change is made, you have to make that change in every page that includes that code. That may not be such a big problem if we are only going to create 3 or 4 pages but it is still a nuisance and it becomes more of a nuisance as we add further pages. To avoid problems like that, it makes sense to take code (or more properly, markup) out of the page and put it into a file where it can be accessed by any page that needs it. Then, if we want to make a change, we only have to make the change in the common file."))
article.append(addParagraph("To see how this works, we will take the code in the head section of our index.php file and place it in a file called head.php and we will put this in a folder called includes so that it can be called by any file on our site. That is, all of the code between <head> and </head> goes into the head.php. The tags themselves stay in the index.php file, but now there is nothing between them. If we save the file and reload the page, we will see what effect removing this code has and this is shown below in figure 24."))
article.append(addImageWithCaption("./images/headless.png", "Figure 24 - the index.php file after removing the head element."))
article.append(addParagraph("If you look at the code we copied over to the head.php file, you will see that this is basically just adding links to our CSS files, so what you are seeing in figure 24 is actually the index.php file without any styling. To get it back, we will need to add some code between our head tags to call in the code from the head.php file, like this."))
article.append(addInset("<?php include 'includes/head.php' ?>"))
article.append(addParagraph("We can save and reload the page again and we will see that it is back to normal only this time, the statements that are creating links to the CSS files are in head.php and are being called by the index.php page."))
article.append(addParagraph("We can then repeat that process until the index.php is reduced to being not much more than a series of calls to other files which makes the index.php file itself more compact and maintainable and keeps cide intended for various purposes in smaller files only concerned with that single process. I have put a copy of the code for index.php into webpage and this includes most of the elements found in the index.php file so we have the same head, reservation form, footer and so on. They are not really needed here but I have kept them in to demonstrate an important point about reusing code in this way. Figure 25 shows what we see when viewing the page."))
article.append(addImageWithCaption("./images/index_php.png", "Figure 25 - the index.php file being displayed in an HTML file."))
article.append(addParagraph("You might notice that I scrolled down a bit to show more of the code from the index.php file, but aside from the main section, this doesn't look any different from the index.php file. It also has the registration form at the top and the footer at the bottom and this raises the question, do we really need all of that in this page. The answer is probably not, but keeping common elements is intended to make all of the pages on a site have a common look and that certainly seems to be the case here. We probably don't need the navbar which is visible in figure 25. However, it doesn't do anything here because it is linking to various page elements, but those elements are on the index.php page. Here, we are linking to these elements using their IDs, but those IDs don't exist on this page and so clicking them does nothing."))
article.append(addParagraph("Let's assume that we want to keep the navbar, the logic being that if this were a real project, we might want users to be able to jump directly to a section in the index page. For example, let's say that we want them to be able to go the attractions section from here, the link we have at the monent is"))
article.append(addInset('<li><a class="icon attractions" href="#attractions"><span>attractions</span></a></li>'))
article.append(addParagraph("So this is linking to an element in the current page that doesn't exist. To make it link to the element in the index.php page, which does exist, we need to specify both the page and the id in the link, so this becomes"))
article.append(addInset('<li><a class="icon attractions" href="index.php#attractions"><span>attractions</span></a></li>'))
article.append(addParagraph("Clearly, adding the name of the file to our links mad the code a little bit more portable and this illustrates a point about development. The first page for the project was linking to different parts within the same page. When we moved the code out of the page and into one of the files in our includes folder, the code still worked for that page but didn't work when added to another page. It is important to remember that when writing resuable code, you don't want to wtite it just so that it works for the page you are currently creating, you also want to ensure that it will always work for any scenario where it may be used in future. Strictly speaking, we haven't really done that here because the links are using relative paths and this means that they will work for a file that is in the same folder as the index.php file. If we want to ensure that it is always going to work, we really should use an absolute path which would look something like this."))
article.append(addInset("<?php include '/webdevelopment/gsfullstack/project/end/includes/head.php' ?>"))
article.append(addParagraph("Note that this path actually wouldn't work at the moment although it would likely be fine at some point in the future due to the fact that I currently have the gsfullstack pages in a temp folder which I will move over to the webdevelopment folder when it has been completed. This sort of raises another interesting point in that it is usually a good idea to position files where they will be as accessible as needed. As an example, I have some CSS files for this site that are used by all pages in the site so these go into a css directory at the root level. On the other hand, I have some CSS files that I only use for wed development pages so these go into a css folder inside the webdevelopment directory."))
article.append(addParagraph("Essentially, any files that are going to be used site-wide should be gathered into a directory at root level. For example, if the project were a real project, it would be the only project in the sites root directory and the includes folder would be at that root level. If we did that, in fact, our absolute path for those includes files would be"))
article.append(addInset("<?php include '/includes/head.php' ?>"))
article.append(addParagraph("For the sake of clarity, I should say that when I say root as in the root level, this should not be confused with the web servers root directory. In this context, root means the root fir the site, or the directory in which the entire site is stored."))
article.append(addParagraph("You may be aware that the apache config files include a directive called DocumentRoot which specifies where the files for a particular website are stored. As an example, figure 26 shows the contents of the file 000-default.conf which is the configuration file for the default website if your web server is Apache and it is running on a Debian system, like my Raspberry Pi. This is stored in the /etc/apache2/sites-enabled folder."))
article.append(addImageWithCaption("./images/document_root.png", "Figure 26 - the contents of the 000-default.conf file."))
article.append(addParagraph("As you can see in figure 26, DocumentRoot has a value of /var/www/html which means that everything in this folder is available to the default website. For example, the sites default page, index.html, goes into this folder so when you go to a browser and put osztromok.com into the address bar, this sends a request to my webserver for that default page and the server looks for it in the /var/www/html. Assuming it finds a file with the default name - index.html or index.php - it will server up that page to the user's browser."))
article.append(addParagraph("If I want to edit that page directly into the browser, I would do that with a command like"))
article.append(addInset("sudo vim /var/www/html/index.html."))
article.append(addParagraph("Another point I want to make here is that I have used the word code quite often in this section when referring to HTML and this is not really accurate in the sense that HTMS is not a programming language, it's a markup language so it should be referred to as markup but I just thought it would be easier and less confusing to refer to it as code."))
article.append(addHeader("Create a Confirmation Page"))
article.append(addParagraph("In the project website, we can insert the details for a reservation and then click the Reserve button and we get the result as shown in figure 27."))
article.append(addImageWithCaption("./images/reserve.png", "Figure 27 - the result of clicking the Reserve button."))
article.append(addParagraph("There are two problems with this. The first if that the result looks very plain and is not in keeping with the rest of the site. The second is that it doesn't actually do anything other than let us know that the button has been clicked and the details we provided are as shown. In this section, we will solve the first problem and in the next, we will solve the second."))
article.append(addParagraph("If we look at the reserve.php file as it stands (in the exercise files, this is in the end folder which is in the folder 08_02-projectconfirm), we can see that this consists of just a few lines of PHP that echo the values passed to it by index.php. In other words, it just takes the values from the reservation form and outputs them to the screen. In order to tidy it up, we are going to merge this file with the index.php file. What I mean by that is that essentially, we will copy all of the code from index.php and paste it in to reserve.php but we will replace the main section with the code that echoes the reservation details. So this is pretty much what we did with our index_php.php file. We will also remove the section that calls branding-banner.php so that we don't have to scroll down the page to see the output."))
article.append(addParagraph("Since our reserve.php file now has includes the code from head.php, it is being linked to the same CSS files used by index.php and the result is that the output looks more consistent as shown in figure 28."))
article.append(addImageWithCaption("./images/reserve_styled.png", "Figure 28 - the result of clicking the Reserve button with the head.php file included."))
article.append(addParagraph("In an earlier video, we saw a file called dbconnect.php which inserts a reservation into the database but as it stands, there are two problems with it, both of which we are now going to fix. The first is that it is not connected to the database and the second is that it writes literal values. For example, the name it writes to the database is 'Chalmers', regardless of what name is provided in the form. We will make sure the database is connected and then run the query to insert this literal data into the database. Once that is done, we will fix it so that the data we enter into the form is inserted into the database."))
article.append(addParagraph("Let's quickly review the flow that we want to see when a reservation is made."))
article.append(addInset("Step 1 - the user inputs a name (nameres), start date (startres) and end date (endres)."))
article.append(addInset("Step 2 - the data is written to the database (via dbconnect.php)."))
article.append(addInset("Step 3 - the data is written to the browser (via reserve.php)."))
article.append(addParagraph("Since we want to write the data to the database before we write it to the browser, a good place to do that is in reserve.php just before we send the data to our browser. We will add the line of code"))
article.append(addInset("<?php include 'php/dbconnect.php'; ?>"))
article.append(addParagraph("just before the main element. Next, we want to save and test this. Note that I have just recreated the database in preparation for this exercise so it is currently empty as you can see in figure 29."))
article.append(addImageWithCaption("./images/empty_database.png", "Figure 29 - the reservations table is currently empty."))
article.append(addParagraph("If I now type a name, let's say Smith, into the reservation form along with a date (as I write this, the date is 19 September 2022 so that's the start date and 20 September 2022 is the end date) and click Reserve, this should now be adding something to the database. If I run the same query as shown in figure 29, I do indeed see that the database has been updated as seen in figure 30."))
article.append(addImageWithCaption("./images/chalmers.png", "Figure 30 - the updated reservations table."))
article.append(addParagraph("As I mentioned earlier, the query used to update the table actually has values already hard-coded in to it and so these are the values inserted into the table rather than the values we actually inserted into the form. As I mentioned previously, when the Reserve button is clicked, the POST method is used to pass three variable over to the reserve.php file, so we want to insert these into the query. To do this, I will create a couple of varialbes to grab the data sent to reserve.php, so that is"))
article.append(addInset("$nameres = $_POST['nameres'];"))
article.append(addParagraph("for the name and"))
article.append(addInset("$startres = $_POST['startres'];"))
article.append(addParagraph("for the date. We can then add these into the query which will now look like this."))
article.append(addInset("INSERT INTO `reservations` (`name`, `startdate`) VALUES ('$nameres', '$startres');"))
article.append(addParagraph("If I save and reload the page and then insert my own name (Osztromok) into the form with a date of 9 December 2022 and click the Reserve button, the correct details are entered into the database and you can see that in figure 31."))
article.append(addImageWithCaption("./images/osztromok.png", "Figure 31 - the reservations table updated with data from the reservation form."))
article.append(addParagraph("There is one other problem we haven't mentioned yet and that is the fact that we are only storing the start date for the booking and you may have wondered why we didn't also store the end date. The reason is simply that we don't need the end date, we just need to know when there is a booking. For example, let's keep things simple by assuming the hotel only has one room and a guest makes a reservation from the 9th to the 12th of December. We need to insert all of these dates, 9th, 10th, 11th and 12th into the database so that the room is reserved for these dates. If we stored the start and end dates, we would need to add some method of calculating whether a given date is between the start and end dates of an existing reservation and that is unnecessarily complicated."))
article.append(addParagraph("We are not going to go into the method of doing that in any great detail here. For reference, the solution to this problem can be found in stackoverflow.com. Essentially, we are going to take both the start and end dates and use then to control a while loop that inserts a record into the database for every day covered by the reservation."))
article.append("For reference, the code that does this is shown in figure 32.")
article.append(addImageWithCaption("./images/osztromok.png", "Figure 32 - the while loop in dbconnect.php used to store a range of dates."))
article.append(addParagraph("So you can see that we are we are iterating through the loop using a value of day which is derived from the value of startres, and we are continuing to iterate while the startdate is less than the end date. Although it looks a little strange, we can infer the fact that the line"))
article.append(addInset("$day->add(new DateInterval('P1D'));"))
article.append(addParagraph("Is adding a day to the $day variable so we are recording a reservation for today's date, moving on to the next day's date and continuing that as long as the date we are recording is less than the end date. I have saved this and I will test it by entering my own name again and providing a start date of 10th December and an end date of 12 December. We can then look at the database again and we can see that there are 4 rows in the database, one for Chalemrs which was inserted during our intial test, one for Osztromok when I was testing writing data from the form to the database and two from when I tested inserting a date range. The current state of the database can be seen in figure 33."))
article.append(addImageWithCaption("./images/date_range.png", "Figure 33 - the database with 4 rows now added to it."))
article.append(addHeader("Outsmart Hackers and Overcome User Error"))
article.append(addParagraph("That completes our project and this finished project can be seen here, although I am sure you will realise that it is a long way from being something that is ready for deployment on the internet. For instance, there is nothing to prevent a user from entering something they are not supposed to and that could be because they have made a mistake (for instance by entering a start date that is after the end date) or because they are deliberately trying to attack the site (such as with an SQL injection attack)."))
article.append(addParagraph("Earlier, we mentioned a flow of actions for a reservation which we will repeat here with one change."))
article.append(addInset("Step 1 - the user inputs a name (nameres), start date (startres) and end date (endres)."))
article.append(addInset("Step 2 - filter the user input until it is ready to be written to the database."))
article.append(addInset("Step 3 - the data is written to the database (via dbconnect.php)."))
article.append(addInset("Step 4 - the data is written to the browser (via reserve.php)."))
article.append(addParagraph("To illustrate how important it is to filter the inputs, imagine a user enters 12 December as the start date and 9 December as the end date. They will still see a confirmation oin the screen, but since the start date is not less than the end date, the condition in our while loop (in dbconnect.php) is never true and so the reservation is not written to the database. If this were to happen on a live site, this could mean guests turning up to the hotel without having actually made a reservaiton!"))
article.append(addParagraph("Security for our website is huge topic and it is not covered here. There are hundreds of course on LinkedIn Learning realting to computer security in general, but a good one for web development is Programming Foundations: Web Security by Kevin Skoglund, published in May 2019, which covers filtering of inputs as well as common attacks including SQL injection attacks."))
main.append(menu("fullstack"))