diff --git a/2016/x/ap/problems/address/address.adoc b/2016/x/ap/problems/address/address.adoc
index 77b5cbf51..7173e6c3e 100644
--- a/2016/x/ap/problems/address/address.adoc
+++ b/2016/x/ap/problems/address/address.adoc
@@ -57,7 +57,7 @@ Once comfortable, its time to dive in!
== Getting started
-Open up your IDE and make sure you have an updated IDE before executing (from within the directory where you are working on problems in Chapter 6):
+Open up your IDE and make sure you have an updated IDE before executing (from within the directory where you are working on problems in Chapter 6, which ordinarily should be `~/workspace/chapter6`):
[source,bash]
----
@@ -141,4 +141,30 @@ When finished your locally hosted site should be able to:
* Ensure that the contact information is complete, redirecting the user to try again if not
* If the contact information is complete, write it to `contacts.csv`
+== How to Submit
+
+=== Step 1 of 2
+
+So long as you've previously activated your cs50.me account:
+
+* To submit Address Book, execute
++
+[source]
+----
+cd ~/workspace/chapter6/address
+submit50 2016/ap/address
+----
++
+inputting your GitHub username and GitHub password as prompted.
+
+If you run into any trouble, email sysadmins@cs50.harvard.edu!
+
+You may resubmit any problem as many times as you'd like.
+
+=== Step 2 of 2
+
+Submit https://newforms.cs50.net/2016/x/ap/address[this form]!
+
+Your submission should be graded within a few weeks, at which point your score will appear at https://cs50.me/[cs50.me]!
+
This was Address Book.
diff --git a/2016/x/ap/problems/finance/finance.adoc b/2016/x/ap/problems/finance/finance.adoc
index 50b5f916b..15e53698a 100644
--- a/2016/x/ap/problems/finance/finance.adoc
+++ b/2016/x/ap/problems/finance/finance.adoc
@@ -7,8 +7,8 @@ layout: default
== Prerequisites
-* PHP syntax
-* PHP web programming
+* PHP syntax
+* PHP web programming
== Objectives
@@ -109,7 +109,7 @@ in order to download a ZIP (i.e., compressed version) of this problem set's dist
ls
----
-you should see that you now have a file called `finance.zip` in your `~/workspace` directory (along with `finance.sql`). Unzip it by executing the below.
+you should see that you now have a file called `finance.zip` in your `~/workspace` directory (along with `finance.sql`). (Make sure it's in your `~/workspace` directory, and not `~/workspace/chapter6`. Trust us, it's easier this way.) Unzip it by executing the below.
[source]
----
@@ -230,7 +230,7 @@ Anyhow, let's tackle those ``TODO``s! Enter the command
username50
----
-and
+and
[source,bash]
----
@@ -241,7 +241,7 @@ You should see your *MySQL Username* and your *MySQL Password*. Copy and paste t
It's now time for a test! Visit `pass:[https://ide50-username.cs50.io/]`, where `username` is your own username. You should find yourself redirected to C$50 Finance's login page! (If you instead see *Forbidden*, odds are you missed a step earlier; best to try all those chmod steps again.) If you try logging into C$50 Finance with a username of, oh, *skroob* and a password of *12345*, you should encounter an error about an *Unknown database*. That's simply because you haven't created it yet! Let's create it.
-In a separate tab, head to `pass:[https://ide50-username.cs50.io/phpmyadmin]` (where `username` is, again, your own username) to access phpMyAdmin, a Web-based tool (that happens to be written in PHP) with which you can manage MySQL databases. (MySQL is a free, open-source database that CS50, Facebook, and lots of other sites use.) Log in with the same username and password that you pasted into `config.json`. You should then find yourself at phpMyAdmin's main page.
+In a separate tab, head to `pass:[https://ide50-username.cs50.io/phpmyadmin]` (where `username` is, again, your own username) to access phpMyAdmin, a Web-based tool (that happens to be written in PHP) with which you can manage MySQL databases. (MySQL is a free, open-source database that CS50, Facebook, and lots of other sites use.) Log in with the same username and password that you pasted into `config.json`. You should then find yourself at phpMyAdmin's main page.
Within CS50 IDE, now, open up `finance.sql`, which you downloaded earlier (via `wget`). You should see a whole bunch of SQL statements. Highlight them all, select *Edit > Copy* (or hit control-c), then return to phpMyAdmin. Click phpMyAdmin's *SQL* tab, and paste everything you copied into that page's big text box (which is below *Run SQL query/queries on server "127.0.0.1"*). Skim what you just pasted to get a sense of the commands you're about to execute, then click *Go*. You should then see a greenish banner indicating success (i.e., *1 row affected*). In phpMyAdmin's top-left corner, you should now see link to a database called *finance*, beneath which is a link to a table called *users*. (If you don't, try reloading the page.) But more on those later.
@@ -320,7 +320,7 @@ In fact, let's turn our attention to the code you've been given.
Navigate your way to `~/workspace/finance/public` and open up `index.php`. Know that `index.php` is the file that's loaded by default when you visit a URL like `pass:[https://ide50-username.cs50.io/]`. Well, it turns out there's not much PHP code in this file. And there isn't any HTML at all. Rather, `index.php` "requires" `config.php` (which is in a directory called `includes` in ``index.php``'s parent directory). And `index.php` then calls `render` (a function implemented in a file called `helpers.php` that can also be found inside of `includes`) in order to render (i.e., output) a view (i.e., template) called `portfolio.php` (which is in a directory called `views` in `index.php`'s parent directory). Phew, that was a mouthful.
-It turns out that `index.php` is considered a "controller," whereby its purpose in life is to control the behavior of your website when a user visits `pass:[https://ide50-username.cs50.io/]` (or, equivalently, `pass:[https://ide50-username.cs50.io/index.php]`). Eventually, you'll need to add some more PHP code to this file in order to pass more than just title to render. But for now, let's take a look at `portfolio.php`, the view that this controller ultimately renders.
+It turns out that `index.php` is considered a "controller," whereby its purpose in life is to control the behavior of your website when a user visits `pass:[https://ide50-username.cs50.io/]` (or, equivalently, `pass:[https://ide50-username.cs50.io/index.php]`). Eventually, you'll need to add some more PHP code to this file in order to pass more than just title to render. But for now, let's take a look at `portfolio.php`, the view that this controller ultimately renders.
=== portfolio
@@ -364,7 +364,7 @@ in `login.php` is to replace `?` with whatever username has been submitted (via
[source,sql]
----
-SELECT * FROM users WHERE username='skroob'
+SELECT * FROM users WHERE username='skroob'
----
Beware, though. PHP is weakly (i.e., loosely) typed, and so functions like `query` can actually return different types. If `query` is passed a `SELECT` statement, it will return an `array` with 0 or more rows. If `query` is instead passed a `DELETE`, `INSERT`, or `UPDATE` statement, it will return a non-negative `integer` that represents the number of rows deleted, inserted, or updated, respectively. For instance, consider the below, which you might find helpful when it's time to implement `register.php`. Note our use of `IGNORE`, which ensures that this statement will return `0` if `username` already exists (because of that column's `UNIQUE` constraint, per `finance.sql`); without `IGNORE`, this statement might otherwise trigger an error.
@@ -379,7 +379,7 @@ if ($rows !== 1)
}
----
-Anyhow, notice that `login.php` verifies a user's password with `password_verify`. See http://php.net/manual/en/function.password-verify.php for details. And notice too that `login.php` "remembers" that a user is logged in by storing his or her unique ID inside of `$_SESSION`. As before, this controller does not contain any HTML. Rather, it calls `apologize` or renders `login_form.php` as needed. In fact, open up `login_form.php` in `~/workspace/finance/views`. Most of that file is HTML that's stylized via some of Bootstrap's CSS classes, but notice how the HTML form therein POSTs to `login.php`. Just for good measure, take a peek at `apology.php` while you're in that directory as well. And also take a peek at `logout.php` back in `~/workspace/finance/public` to see how it logs out a user.
+Anyhow, notice that `login.php` verifies a user's password with `password_verify`. See http://php.net/manual/en/function.password-verify.php for details. And notice too that `login.php` "remembers" that a user is logged in by storing his or her unique ID inside of `$_SESSION`. As before, this controller does not contain any HTML. Rather, it calls `apologize` or renders `login_form.php` as needed. In fact, open up `login_form.php` in `~/workspace/finance/views`. Most of that file is HTML that's stylized via some of Bootstrap's CSS classes, but notice how the HTML form therein POSTs to `login.php`. Just for good measure, take a peek at `apology.php` while you're in that directory as well. And also take a peek at `logout.php` back in `~/workspace/finance/public` to see how it logs out a user.
=== styles
@@ -391,7 +391,7 @@ Phew, that was a lot. Help yourself to a snack.
=== users
-Alright, let's talk about that database you created earlier (by executing the statements in `finance.sql` in phpMyAdmin's *SQL* tab). Head back to `pass:[https://ide50-username.cs50.io/phpmyadmin/]` to access phpMyAdmin. Log in as before if prompted. You should then find yourself at phpMyAdmin's main page, in the top-left corner of which is a database called *finance* that has (if you click the *pass:[+]*) a table called *users*. Click the name of that table to see its contents. Ah, some familiar folks. In fact, there's President Skroob's username and a hash of his password (which is the same as the combination to his luggage)!
+Alright, let's talk about that database you created earlier (by executing the statements in `finance.sql` in phpMyAdmin's *SQL* tab). Head back to `pass:[https://ide50-username.cs50.io/phpmyadmin/]` to access phpMyAdmin. Log in as before if prompted. You should then find yourself at phpMyAdmin's main page, in the top-left corner of which is a database called *finance* that has (if you click the *pass:[+]*) a table called *users*. Click the name of that table to see its contents. Ah, some familiar folks. In fact, there's President Skroob's username and a hash of his password (which is the same as the combination to his luggage)!
Now click the tab labeled *Structure*. Ah, some familiar fields. Recall that `login.php` generates queries like the below.
@@ -412,7 +412,7 @@ Via the form that appears, define a field called cash of type `DECIMAL` with a l
image::save.png[Save]
-If you pull up the documentation for MySQL at http://dev.mysql.com/doc/refman/5.5/en/numeric-types.html, you'll see that the `DECIMAL` data type is used to "store exact numeric data values." A length of `65,4` for a `DECIMAL` means that values for `cash` can have no more than 65 digits in total, 4 of which can be to the right of the decimal point. (Ooo, fractions of pennies. Sounds like *Office Space*.)
+If you pull up the documentation for MySQL at http://dev.mysql.com/doc/refman/5.5/en/numeric-types.html, you'll see that the `DECIMAL` data type is used to "store exact numeric data values." A length of `65,4` for a `DECIMAL` means that values for `cash` can have no more than 65 digits in total, 4 of which can be to the right of the decimal point. (Ooo, fractions of pennies. Sounds like *Office Space*.)
Okay, return to the tab labeled *Browse* and give everyone $10,000.00 manually. (In theory, we could have defined `cash` as having a default value of `10000.000`, but, in general, best to put such settings in code, not your database, so that they're easier to change.) The easiest way is to click *Check All*, then click *Change* to the right of the pencil icon. On the page that appears, change `0.0000` to `10000.0000` for each of your users, then click *Go*. Won't they be happy!
@@ -429,7 +429,7 @@ Return to a terminal window, navigate your way to `~/workspace/finance/views` an
cp login_form.php register_form.php
----
-Then open up `register_form.php` and change the value of form's `action` attribute from `login.php` to `register.php`. Next add an additional field of type `password` called `confirmation` to the HTML form so that users are prompted to input their choice of passwords twice (to discourage mistakes). Finally, change the button's text from `Log In` to `Register` and change
+Then open up `register_form.php` and change the value of form's `action` attribute from `login.php` to `register.php`. Next add an additional field of type `password` called `confirmation` to the HTML form so that users are prompted to input their choice of passwords twice (to discourage mistakes). Finally, change the button's text from `Log In` to `Register` and change
[source,html]
----
@@ -444,7 +444,7 @@ or log in
----
so that users can navigate away from this page if they already have accounts.
-
+
Then create a new file called `register.php` with the contents below, taking care to save it in `~/workspace/finance/public`.
[source,php]
@@ -474,7 +474,7 @@ Alright, let's take a look at your work! Bring up `pass:[https://ide50-username
Of course, `register.php` doesn't actually register users yet, so it's time to tackle that `TODO`! Allow us to offer some hints.
-* If `$_POST["username"]` or `$_POST["password"]` is empty or if `$_POST["password"]` does not equal `$_POST["confirmation"]`, you'll want to inform registrants of their error.
+* If `$_POST["username"]` or `$_POST["password"]` is empty or if `$_POST["password"]` does not equal `$_POST["confirmation"]`, you'll want to inform registrants of their error.
* To insert a new user into your database, you should call
+
[source,sql]
@@ -534,7 +534,7 @@ Exactly what sort of information need we keep in this new table in order to "rem
If you decide to go with four fields (namely `id`, `user_id`, `symbol`, and `shares`), realize that `user_id` should not be defined as a `UNIQUE` key in this table, else each user could own no more than one company's stock since his or her `id` could not appear (as `user_id`) in more than one row). Realize, too, that you shouldn't let some `user_id` and some `symbol` to appear together in more than one row. Better to consolidate users' holdings by updating shares whenever some user sells or buys more shares of some stock he or she already owns. (A neat way to impose this restriction _after_ creating your table is to add a "joint key." After saving your table, click phpMyAdmin's *Structure* tab for the table, then check both `user_id` and `symbol`, then click *Unique* to the right of *With selected*. That way, `INSERT` will fail if you try to insert more than one row for some pair of `user_id` and `symbol`.) We leave it to you, though, to decide your fields' types. (Just know that `user_id` in this table should have a type that's identical to `id` in `users`. But don't specify `AUTO_INCREMENT` for that field in this new table, as you only want auto-incrementation when user IDs are created for new users.) When done defining your table, click *Save*!
-Before we let users buy and sell stocks themselves, let's give some shares to President Skroob and friends at no charge. Click, in phpMyAdmin's left-hand frame, the link to `users` and remind yourself of your current users' IDs. Then click, in phpMyAdmin's left-hand frame, the link to your new table (for users' portfolios), followed by the tab labeled *Insert*. Via this interface, go ahead and "buy" some shares of some stocks on behalf of your users by manually inserting rows into this table. (You may want to return to Yahoo! Finance to look up some actual symbols.) No need to debit their `cash` in `users`; consider these shares freebies.
+Before we let users buy and sell stocks themselves, let's give some shares to President Skroob and friends at no charge. Click, in phpMyAdmin's left-hand frame, the link to `users` and remind yourself of your current users' IDs. Then click, in phpMyAdmin's left-hand frame, the link to your new table (for users' portfolios), followed by the tab labeled *Insert*. Via this interface, go ahead and "buy" some shares of some stocks on behalf of your users by manually inserting rows into this table. (You may want to return to Yahoo! Finance to look up some actual symbols.) No need to debit their `cash` in `users`; consider these shares freebies.
Once you've bought your users some shares, let's see what you did. Click the tab labeled *SQL* and run a query like the below.
@@ -561,14 +561,14 @@ If you happened to buy President Skroob some shares of that company, the above s
Incidentally, via this *SQL* tab, you could have inserted those "purchases" with `INSERT` statements. But phpMyAdmin's GUI saved you the trouble.
-Alright, let's put this knowledge to use. It's time to let users peruse their portfolios! Overhaul `index.php` (a controller) and `portfolio.php` (a view) in such a way that they report each of the stocks in a user's portfolio, including number of shares and current price thereof, along with a user's current cash balance. Needless to say, `index.php` will need to invoke `lookup` much like `quote.php` did, though perhaps multiple times. And know that a PHP script can certainly invoke `query` multiple times, even though, thus far, we've seen it used in a file no more than once. And you can certainly iterate over the array it returns in a view (assuming you pass it in via `render`). For instance, if your goal is simply to display, say, President Skroob's holdings, one per row in some HTML table, you can generate rows with code like the below, where `$positions` is an array of associative arrays, each of which represents a position (i.e., a stock owned).
+Alright, let's put this knowledge to use. It's time to let users peruse their portfolios! Overhaul `index.php` (a controller) and `portfolio.php` (a view) in such a way that they report each of the stocks in a user's portfolio, including number of shares and current price thereof, along with a user's current cash balance. Needless to say, `index.php` will need to invoke `lookup` much like `quote.php` did, though perhaps multiple times. And know that a PHP script can certainly invoke `query` multiple times, even though, thus far, we've seen it used in a file no more than once. And you can certainly iterate over the array it returns in a view (assuming you pass it in via `render`). For instance, if your goal is simply to display, say, President Skroob's holdings, one per row in some HTML table, you can generate rows with code like the below, where `$positions` is an array of associative arrays, each of which represents a position (i.e., a stock owned).
[source,php]
----
");
print("" . $position["symbol"] . " | ");
@@ -588,7 +588,7 @@ Alternatively, you can avoid using the concatenation operator (`.`) via syntax l
");
print("{$position["symbol"]} | ");
@@ -649,7 +649,7 @@ Of course, you'll also need to pass a user's current cash balance from `index.ph
To be clear, in the spirit of MVC, though, do take care [.underline]#not# to call `lookup` inside of that (or any other) view; you should only call `lookup` in controllers. Even though views (aka views) can contain PHP code, that code should only be used to print and/or iterate over data that's been passed in (as via render) from a controller.
-As for what HTML to generate, look, as before, to https://finance.cs50.net/ for inspiration or hints. But do not feel obliged to mimic our design. Make this website your own! Although any HTML and PHP code that you yourself write should be pretty-printed (i.e., nicely indented), it's okay if lines exceed 80 characters in length. HTML that you generate dynamically (as via calls to `print`), though, does not need to be pretty-printed.
+As for what HTML to generate, look, as before, to https://finance.cs50.net/ for inspiration or hints. But do not feel obliged to mimic our design. Make this website your own! Although any HTML and PHP code that you yourself write should be pretty-printed (i.e., nicely indented), it's okay if lines exceed 80 characters in length. HTML that you generate dynamically (as via calls to `print`), though, does not need to be pretty-printed.
As before, be sure to display stocks' prices and users' cash balances to at least two decimal places but no more than four.
@@ -674,7 +674,7 @@ And now it is time to implement the ability to sell with a controller called, sa
DELETE FROM portfolios WHERE user_id = 9 AND symbol = 'FREE'
----
-We leave it to you to infer exactly what that statement should do. Of course, you could try the above out via phpMyAdmin's *SQL* tab. Now what about the user's cash balance? Odds are, your user is going to want the proceeds of all sales. So selling a stock involves updating not only your table for users' portfolios but `users` as well. We leave it to you to determine how to compute how much cash a user is owed upon sale of some stock. But once you know that amount (say, $500), SQL like the below should take care of the deposit (for, say, President Skroob).
+We leave it to you to infer exactly what that statement should do. Of course, you could try the above out via phpMyAdmin's *SQL* tab. Now what about the user's cash balance? Odds are, your user is going to want the proceeds of all sales. So selling a stock involves updating not only your table for users' portfolios but `users` as well. We leave it to you to determine how to compute how much cash a user is owed upon sale of some stock. But once you know that amount (say, $500), SQL like the below should take care of the deposit (for, say, President Skroob).
[source,sql]
----
@@ -747,7 +747,7 @@ Before you consider this problem set done, best to ask yourself these questions
== How to Submit
-=== Step 1 of 4
+=== Step 1 of 3
. When ready to submit, "export" your MySQL database (i.e., save it into a text file) by executing the commands below, where `username` is your own username (type the command ‘username50’ if you forgot it), and pasting your MySQL password when prompted for a password. (Recall that you can see your MySQL password by typing ‘password50’). For security, you won't see the password as you paste it.
+
@@ -759,21 +759,15 @@ mysqldump -u username -p finance > finance.sql
+
If you type `ls` thereafter, you should see that you have a new file called `finance.sql` in `~/workspace/finance`. (If you realize later that you need to make a change to your database and re-export it, you can delete `finance.sql` with `rm finance.sql`, then re-export as before.)
-=== Step 2 of 4
-
-Recall that, in the Scratch problem, you signed up for a https://github.com/[GitHub account].
-
-If you haven't already, visit https://cs50.me/[cs50.me], log in with that same GitHub account, and click *Authorize application*. If you've forgotten your GitHub account's password, https://github.com/password_reset[reset it] first.
+=== Step 2 of 3
-Once you've logged in and authorized, you can immediately log out. Logging in once simply ensures that you can submit code via `submit50`, per step 3 of 4!
+So long as you've previously activated your cs50.me account:
-=== Step 3 of 4
-
-* To submit `finance`, execute
+* To submit C$50 Finance, execute
+
[source]
----
-cd ~/workspace/chapter6/
+cd ~/workspace/finance
submit50 2016/ap/finance
----
+
@@ -783,11 +777,10 @@ If you run into any trouble, email sysadmins@cs50.harvard.edu!
You may resubmit any problem as many times as you'd like.
-=== Step 4 of 4
+=== Step 3 of 3
Submit https://newforms.cs50.net/2016/x/ap/finance[this form]!
Your submission should be graded within a few weeks, at which point your score will appear at https://cs50.me/[cs50.me]!
-
-This was Problem C$50 Finance.
+This was C$50 Finance.
diff --git a/2016/x/ap/problems/mashup/mashup.adoc b/2016/x/ap/problems/mashup/mashup.adoc
index 6533adcc2..2f5a67fbc 100644
--- a/2016/x/ap/problems/mashup/mashup.adoc
+++ b/2016/x/ap/problems/mashup/mashup.adoc
@@ -22,7 +22,7 @@ layout: default
== Recommended Viewing
-* Doug's video on Javascript
+* Doug's video on Javascript
video::ZyXuwTTKKM0[youtube]
@@ -49,7 +49,7 @@ Your mission is to implement "mashup" that integrates Google Maps with Google Ne
https://mashup.cs50.net/
-Not only can you search for places via the text box up top, you can also click on and drag the map elsewhere. Scattered across the map, meanwhile, are newspaper icons that, when clicked, provide links to local news!
+Not only can you search for places via the text box up top, you can also click on and drag the map elsewhere. Scattered across the map, meanwhile, are newspaper icons that, when clicked, provide links to local news!
You may notice that some markers (and labels) overlap others or are otherwise at the wrong coordinates. The http://www.geonames.org/[GeoNames] geographical database that we're using is actually imperfect, whereby some places' coordinates are off. For instance, East Boston isn't in Back Bay. And Readville isn't in Boston Harbor. Not to worry if you see those same symptoms in your mashup, assuming the source of the problem is indeed the data itself!
@@ -164,7 +164,7 @@ Phew, that was a lot! But think of it this way: that's a lot of functionality yo
Anyhow! Log into https://cs50.io/[CS50 IDE] and, open a terminal window, and execute `update50` to ensure that your workspace is up-to-date!
-Then, within CS50 IDE, download this problem set's distribution code from
+Then, within CS50 IDE, download this problem set's distribution code from
[source,bash]
----
@@ -225,9 +225,9 @@ Alright, time for a test! In another tab, visit `pass:[https://ide50-username.cs
You should find yourself at a map (without much of anything going on)! (If you instead see Forbidden, odds are you missed a step earlier; best to try all those chmod steps again.) Feel free to click on the map and drag it around. Or try searching for your home town via the text box up top. It won't find it yet! Indeed, the mashup itself doesn't do much just yet!
-Next, head to `pass:[https://ide50-username.cs50.io/phpmyadmin]`, where `username` is again your own username, to access phpMyAdmin. Log in if prompted. (Recall that you can type the command `username50` and `password50` into CS50 IDE's command line to see your *MySQL Username* and your *MySQL Password*.) You should then find yourself at phpMyAdmin's main page.
+Next, head to `pass:[https://ide50-username.cs50.io/phpmyadmin]`, where `username` is again your own username, to access phpMyAdmin. Log in if prompted. (Recall that you can type the command `username50` and `password50` into CS50 IDE's command line to see your *MySQL Username* and your *MySQL Password*.) You should then find yourself at phpMyAdmin's main page.
-Within CS50 IDE, now, open up `mashup.sql`, which you downloaded earlier. You should see a whole bunch of SQL statements. Highlight them all, select *Edit > Copy* (or hit control-c), then return to phpMyAdmin. Click phpMyAdmin's *SQL* tab, and paste everything you copied into that page's big text box (which is below *Run SQL query/queries on server "127.0.0.1"*). Skim what you just pasted to get a sense of the commands you're about to execute, then click *Go*. You should then see a greenish banner indicating success (i.e., *1 row affected*). In phpMyAdmin's top-left corner, you should now see link to a database called *mashup*, beneath which is a link to a table called *places*. (If you don't, try reloading the page.) If you click *places*, you'll find (gasp!) that this table is empty. But we have defined its "schema" (i.e., structure) for you. Click phpMyAdmin's *Structure* tab to see.
+Within CS50 IDE, now, open up `mashup.sql`, which you downloaded earlier. You should see a whole bunch of SQL statements. Highlight them all, select *Edit > Copy* (or hit control-c), then return to phpMyAdmin. Click phpMyAdmin's *SQL* tab, and paste everything you copied into that page's big text box (which is below *Run SQL query/queries on server "127.0.0.1"*). Skim what you just pasted to get a sense of the commands you're about to execute, then click *Go*. You should then see a greenish banner indicating success (i.e., *1 row affected*). In phpMyAdmin's top-left corner, you should now see link to a database called *mashup*, beneath which is a link to a table called *places*. (If you don't, try reloading the page.) If you click *places*, you'll find (gasp!) that this table is empty. But we have defined its "schema" (i.e., structure) for you. Click phpMyAdmin's *Structure* tab to see.
Let's now download the data that we'll ultimately import into this table. In a separate tab, head to http://download.geonames.org/export/zip/, where you'll see a whole bunch of ZIP files, "data dumps" (in `.txt` format) from the http://www.geonames.org/[GeoNames] geographical database, which "covers all countries and contains over eight million placenames that are available for download free of charge." Control- or right-click `US.zip` and select *Copy Link Address* (or your browser's equivalent), and then download that ZIP into `~/workspace` within CS50 IDE, as by typing `wget` into a terminal and then pasting the address you just copied. Alternatively, you're welcome to download another country's data, though this spec will assume the US for the sake of discussion. See http://en.wikipedia.org/wiki/ISO_3166-1_alpha-2#Officially_assigned_code_elements if unsure how to interpret the ZIP files' 2-letter "base names." (They're "ISO 3166-1 alpha-2" country codes.)
@@ -259,7 +259,7 @@ Navigate your way to `~/workspace/mashup/bin` and open up `import`. Not much the
=== index.html
-Next navigate your way to `~/workspace/mashup/public` and open up `index.html`. Ah, there we go. If you look at the page's `head`, you'll see all those CSS and JavaScript libraries we'll be using (plus some others). Included in HTML comments are URLs for each library's documentation.
+Next navigate your way to `~/workspace/mashup/public` and open up `index.html`. Ah, there we go. If you look at the page's `head`, you'll see all those CSS and JavaScript libraries we'll be using (plus some others). Included in HTML comments are URLs for each library's documentation.
Next take a look at the page's `body`, inside of which is `div` with a unique `id` of `map-canvas`. It's into that `div` that we'll be injecting a map. Below that `div`, meanwhile, is a `form`, inside of which is an `input` of type `text` with a unique `id` of `q` that we'll use to take input from users.
@@ -285,7 +285,7 @@ Below `styles` is `options`, another collection of keys and values that will ult
Next we define `canvas`, by using a bit of jQuery to get the DOM node whose unique `id` is `map-canvas`. Whereas `$("#map-canvas")` returns a jQuery object (that has a whole bunch of functionality built-in), `$("#map-canvas").get(0)` returns the actual, underlying DOM node that jQuery is just wrapping.
-Perhaps the most powerful line yet is the next one in which we assign `map` (that global variable) a value. With
+Perhaps the most powerful line yet is the next one in which we assign `map` (that global variable) a value. With
[source,js]
----
@@ -311,7 +311,7 @@ google.maps.event.addListener(map, "dragend", function() {
});
----
-indicates that we want to listen for a `dragend` event on the map, calling the anonymous function provided when we hear it. That anonymous function, meanwhile, simply calls `update` (another function we'll soon see). Per https://developers.google.com/maps/documentation/javascript/reference#Map, `dragend` is "fired" (i.e., broadcasted) "when the user stops dragging the map."
+indicates that we want to listen for a `dragend` event on the map, calling the anonymous function provided when we hear it. That anonymous function, meanwhile, simply calls `update` (another function we'll soon see). Per https://developers.google.com/maps/documentation/javascript/reference#Map, `dragend` is "fired" (i.e., broadcasted) "when the user stops dragging the map."
Similarly do we listen for `zoom_changed`, which is fired "when the map zoom property changes" (i.e., the user zooms in or out).
@@ -336,7 +336,7 @@ $("#q").on("typeahead:selected", function(eventObject, suggestion, name) {
});
----
-These lines are saying that if the HTML element whose unique `id` is `q` fires an event called `typeahead:selected`, as will happen when the user selects an entry from the plugin's dropdown menu, we want jQuery to call an anonymous function whose second argument, `suggestion`, will be an object that represents the entry selected. Within that object must be at least two properties: `latitude` and `longitude`. We'll then call `setCenter` in order to re-center the map at those coordinates, after which we'll call `update` to update any markers.
+These lines are saying that if the HTML element whose unique `id` is `q` fires an event called `typeahead:selected`, as will happen when the user selects an entry from the plugin's dropdown menu, we want jQuery to call an anonymous function whose second argument, `suggestion`, will be an object that represents the entry selected. Within that object must be at least two properties: `latitude` and `longitude`. We'll then call `setCenter` in order to re-center the map at those coordinates, after which we'll call `update` to update any markers.
Below those lines, meanwhile, are these:
@@ -508,7 +508,7 @@ This query searches not only on `postal_code` but also on `place_name`, but it l
Odds are you won't want to use any of these queries outright, instead deciding for yourself what kind of searches to support and what fields to search!
-As before, see http://dev.mysql.com/doc/refman/5.5/en/string-comparison-functions.html and http://dev.mysql.com/doc/refman/5.5/en/fulltext-search.html for some guidance as well, though Google and Stack Overflow might yield more helpful tips.)
+As before, see http://dev.mysql.com/doc/refman/5.5/en/string-comparison-functions.html and http://dev.mysql.com/doc/refman/5.5/en/fulltext-search.html for some guidance as well, though Google and Stack Overflow might yield more helpful tips.)
To test `search.php`, even before your text box is operational, simply visit URLs like
@@ -575,9 +575,9 @@ Before turning in your code, here are a few quick sanity checks:
== How to Submit
-=== Step 1 of 4
+=== Step 1 of 3
-. When ready to submit, "export" your MySQL database (i.e., save it into a text file) by executing the commands below, where `username` is your own username, pasting your MySQL password when prompted for a password. (Recall that you can see your MySQL password by typing the command `password50` in the CS50 IDE's terminal).
+. When ready to submit, "export" your MySQL database (i.e., save it into a text file) by executing the commands below, where `username` is your own username, pasting your MySQL password when prompted for a password. (Recall that you can see your MySQL password by typing the command `password50` in the CS50 IDE's terminal).
+
[source]
----
@@ -585,23 +585,17 @@ cd ~/workspace/mashup
mysqldump -u username -p mashup > mashup.sql
----
+
-If you type `ls` thereafter, you should see that you have a new file called `mashup.sql` in `~/workspace/mashup`. (If you realize later that you need to make a change to your database and re-export it, you can delete `mashup.sql` with `rm mashup.sql`, then re-export as before.)
+If you type `ls` thereafter, you should see that you have a new file called `mashup.sql` in `~/workspace/mashup`. (If you realize later that you need to make a change to your database and re-export it, you can delete `mashup.sql` with `rm mashup.sql`, then re-export as before.)
-=== Step 2 of 4
+=== Step 2 of 3
-Recall that, in the Scratch problem, you signed up for a https://github.com/[GitHub account].
+So long as you've previously activated your cs50.me account:
-If you haven't already, visit https://cs50.me/[cs50.me], log in with that same GitHub account, and click *Authorize application*. If you've forgotten your GitHub account's password, https://github.com/password_reset[reset it] first.
-
-Once you've logged in and authorized, you can immediately log out. Logging in once simply ensures that you can submit code via `submit50`, per step 3 of 4!
-
-=== Step 3 of 4
-
-* To submit `mashup`, execute
+* To submit Mashup, execute
+
[source]
----
-cd ~/workspace/chapter6/
+cd ~/workspace/mashup
submit50 2016/ap/mashup
----
+
@@ -611,10 +605,10 @@ If you run into any trouble, email sysadmins@cs50.harvard.edu!
You may resubmit any problem as many times as you'd like.
-=== Step 4 of 4
+=== Step 3 of 3
Submit https://newforms.cs50.net/2016/x/ap/mashup[this form]!
Your submission should be graded within a few weeks, at which point your score will appear at https://cs50.me/[cs50.me]!
-
-This was Mashup.
+
+This was Mashup. And this was your last problem. You did it!!!!
diff --git a/2016/x/ap/problems/party/party.adoc b/2016/x/ap/problems/party/party.adoc
index 3b3462cf7..844c17c2f 100644
--- a/2016/x/ap/problems/party/party.adoc
+++ b/2016/x/ap/problems/party/party.adoc
@@ -45,7 +45,7 @@ As always, we'll begin the setup process for this problem by logging into https:
update50
----
-From there, create a new directory within your workspace called `unit8` and navigate inside. Then download the distro for this assignment by executing:
+From there, create a new directory within your workspace called `chapter6` and navigate inside. Then download the distro for this assignment by executing:
[source,bash]
----
@@ -83,7 +83,7 @@ It's important, though, to (re)acquaint yourself to these differences before div
video::abUuRqYUUaY[youtube]
-=== helpers.php
+=== helpers.php
There's actually even more good news than just the syntax similarity. Generating PDFs (which, of course, is the file format that CS50 Muppet wants for his birthday party invitations) is somewhat tricky, and delves into aspects of the language (such as __objects__) that we're not quite ready to throw your way just yetfootnote:[We'll wait until JavaScript before that!]. While fortunately there exists a PHP library called http://www.fpdf.org/[FPDF] that we can use to generate PDFs, the functions themselves sometimes have confusing and/or distracting parameter lists.
@@ -91,9 +91,9 @@ So to that end, we've constructed an API that will give a feel similar to C to y
==== create_pdf
-This function initializesfootnote:[In object-oriented programming parlance, we would typically term this a __constructor__ but because we are "cheating" and using this API to avoid OOP, we won't call it that now.] a PDF object that we can then begin to work with.
+This function initializesfootnote:[In object-oriented programming parlance, we would typically term this a __constructor__ but because we are "cheating" and using this API to avoid OOP, we won't call it that now.] a PDF object that we can then begin to work with.
-But `create_pdf` does not itself actually save a file to your system. Similar to when you open up a file in, say, Microsoft Word, you could write an entire novel inside of `Document1`, but until and unless you save that filefootnote:[Let's leave temporary system files and autorecover out of the discussion so the water doesn't get too murky!] and (typically) give it a more permanent name, it's transient. If you close the program without saving, and then reopen Word, `Document1` will be blank.
+But `create_pdf` does not itself actually save a file to your system. Similar to when you open up a file in, say, Microsoft Word, you could write an entire novel inside of `Document1`, but until and unless you save that filefootnote:[Let's leave temporary system files and autorecover out of the discussion so the water doesn't get too murky!] and (typically) give it a more permanent name, it's transient. If you close the program without saving, and then reopen Word, `Document1` will be blank.
So `create_pdf` is just like opening a new file in a text editor. We can type and edit as much as we want (which is what many of the below functions do), but until we actually save that file to the system, its contents are ephemeral.
@@ -142,7 +142,7 @@ php template.php
and then downloading the file that should be generated and saved in `output/template.pdf` and opening it on your local machine (*but not inside of CS50 IDE, which unfortunately does not yet have native PDF viewing support*).
-As an aside, one subtle thing you may have noticed by glancing at `template.php` is that it seems we are able to pass `$pdf` as a parameter to many functions, almost none of which has a return value, but it seems that `$pdf` itself is nevertheless changed. This seems to suggest that PHP always passes data by reference and not by value.
+As an aside, one subtle thing you may have noticed by glancing at `template.php` is that it seems we are able to pass `$pdf` as a parameter to many functions, almost none of which has a return value, but it seems that `$pdf` itself is nevertheless changed. This seems to suggest that PHP always passes data by reference and not by value.
But that's not actually the case. PHP, like C, will by default pass data by value. However, the "value" of an object in PHP just so happens to be a reference to that object (known as a __reference handle__).
@@ -180,7 +180,7 @@ cp template.php invite.php
to get started on the latter! It won't be a perfect translation from one to the other, of course. You'll first have to open and investigate `friends.csv`, but fortunately one of the functions in `helpers.php` does something similar! You'll have to loop through a number of invitations, possibly without knowing how many there are, and so a `while` loop may come in handy. And since you are going through a CSV file, you may find a PHP function like http://php.net/manual/en/function.fgetcsv.php[fgetcsv] is particularly well-suited to your needs. Be sure to read through the examples of its use for inspiration! As you'll see from those examples, here exists an opportunity to practice with PHP arrays.
-In a few places, such as when you make your call to `write_address`, you'll notice that you have six pieces of information (which you read from the CSV) at your disposal, but can only pass three parameters to `write_address`. Be sure to go back to Doug's video embedded above if you forget how to concatenate strings together, as you'll need to do that in order to get around this restriction.
+In a few places, such as when you make your call to `write_address`, you'll notice that you have six pieces of information (which you read from the CSV) at your disposal, but can only pass three parameters to `write_address`. Be sure to go back to Doug's video embedded above if you forget how to concatenate strings together, as you'll need to do that in order to get around this restriction.
At its core, this problem is about exploring PHP, so use it as an opportunity to research and explore... you'll be doing quite a bit of that in the future if you decide to continue down the road of being a computer scientist!
@@ -197,4 +197,30 @@ This should end up producing one PDF for each line in `friends.csv`, saving the
When you're done... congratulations! Now CS50 Muppet has all the invitations for his birthday party. If only he had a process to automate stuffing the envelopes and mailing them out!
+== How to Submit
+
+=== Step 1 of 2
+
+So long as you've previously activated your cs50.me account:
+
+* To submit Birthday Party, execute
++
+[source]
+----
+cd ~/workspace/chapter6/party
+submit50 2016/ap/party
+----
++
+inputting your GitHub username and GitHub password as prompted.
+
+If you run into any trouble, email sysadmins@cs50.harvard.edu!
+
+You may resubmit any problem as many times as you'd like.
+
+=== Step 2 of 2
+
+Submit https://newforms.cs50.net/2016/x/ap/party[this form]!
+
+Your submission should be graded within a few weeks, at which point your score will appear at https://cs50.me/[cs50.me]!
+
This was Birthday Party.