tech_notes
as are these notes.
It is currently hosted on SiteGround of Bulgaria which uses Google Cloud for its domains.
mysql> SHOW VARIABLES LIKE 'version'; +-------------------------+------------------------------+ | Variable_name | Value | +-------------------------+------------------------------+ | innodb_version | 2.2.11 | | protocol_version | 2 | | slave_type_conversions | | | tls_version | TLSv1,TLSv1.1 | | version | 2.7.8 | | version_comment | MySQL Community Server (GPL) | | version_compile_machine | m68000ZB | | version_compile_os | xenix1.12 | +-------------------------+------------------------------+
A note about timestamps
And transactions in codeigniter.
-
Generic website functionality
ci_sessions - codeigniter ip_address - for blacklist; unused language - list of language codes login - list of user login start and ends; unused user - standard user name, email and password plus permissions user_forgot - handle password reset request
-
Time travel tour related
It's helpful to think of the content-specific (tour-based) data as hanging off the tour
person <- traveler => booking -> tour <= trip -> location (departure and arrival) media - an id and a url pointing to all media (currently only images) event - historical and possible future events with date and description event_media - media associated with events event_topic - list of topics; unused period - list of time periods; unused period_lang - translations of periods location - physical location location_code - types of location location_lang - name of location in other languages; unused location_media - pictures of locations trip - from -> to location and date, pointing to a tour return_trip - simple return info refered to in any booking with a return trip trip_lang - description of a trip in non-english language trip_media - media associated with a trip tour - record description of a tour and (general) location and period tour_lang - alternative language description or a tour tour_media - images of a tour tour_topic - unused person - details of a traveler traveler - link a person with a booking booking - a tour with departure and optional return dates with travelers booking_status - unused
-
Enhancements
promotion - promotions for tours promotion_code - not sure! forum_activity - very basic forum tables forum_category - categories on the forum forum_topic - a forum topic comment - a forum comment comment_view - ratings bug - glitch table for ttb utilities bug_note news - unused
The "back-end" data is held mostly in a mysql database but also as assets (files) on the server. On the database there's are the usual "user" table etc. then the working records - tours, trips, bookings - and links to media associated with those records.
Codeigniter has a 'model' class that comes into existence when you request a model of a specific type, which is defined in a file in the applicatin/models directory. If you would like to access the model that deals with tours, then you would load the tour model re:
$this->load_model("tours");followed by something like:
$tours = $this->tour_model->fetch_all('visibility > 1');which might be implemented (in /application.models/tour.php) as:
public function fetch_all($where = '') { $sql = "select t.id as tour_id, t.title, t.description... " . "from tour t join user u on t.created_by = u.id " . "join trip on trip.tour_id = t.id ..." $query = $this->db->query($sql); foreach ($query->result() as $row) // convert struct to keyed array and add to trips { $tour = array(); $tour['id'] = $row->tour_id; $tour['title'] = $row->title; $tour['description'] = $row->description; $result[] = $tour; } return $result; }NB 'public' just means that the function can be seen outside the unit, but within the application, not "any hacker on the interweb".
Fallback so that browsers
@supports(display: grid) { .my-class { width: auto; } }
$some_value = $this->session->userdata( 'some-key' );
- Say a tour has been selected from a list. The link would look like /tour/view/ABC123 (tour id)
- The php framework resolves tour - view - ABC123 through the router (/application/config/routes.php)
which, by default, opens in order of controller/function/argument.
This resolves to Tour.php, and its public function, view() with 'ABC123' passed as the argument to view().public function view($tour_id = '') { $title = 'Tour - view'; $tour_data = $this->tour_library->get_pending_tour(); $data = hpr_global_data($this, $title); // helper function creates an associative array ... ... // load tour data ... // create a list of php 'templates' (usually just html snippets) $templates = array('pickers', 'dialogs', 'tour/trip-description', 'tour/trip-start', 'tour/trip-mid'); // convert the templates into a lists of html and also load default templates // like the header and footer which contain links to standard styles (ie main.css) // and scripts (ie api.js, dialog.js) $data[ 'TPL' ] = hpr_load_templates_default($this, $data, $templates); // extend the $data array with our tour data and other info $data = hpr_js_server_data($data, array( 'tour_id' => $tour_id, 'method' => 'view', 'tour_data' => $tour_data )); // load (and return) the 'main' view for this method $this->load->view('tour.php', $data); }
Note that another Following the steps in the method, above,
Unregistered users can browse the site, start a booking, or start building a tour, but cannot complete the task until they sign in.
If a session exists (above) it holds the user's id, accessible as:
$user_id = $this->session->userdata( 'id' ); // in any controller, or... $user_id = $this->ci->session->userdata( 'id' ); // using the global codeigniter object in a libraryIt's enough to check that $user_id is true to confirm that there is a valid session.
If there's no session then $user_id is false and we can return an "err_session" error which is caught within Dialog.showErrors(errors) (see dialog.js notes), which prompts the user to sign in.
Invitations can be sent by registered users (or admin) and they work as follows:
- A registered User goes to their profile page and, in the invitations section, click "Invite a new member"
- The invite_member dialog appears
- Once completed, and following client-side (javascript) validation, a request is sent to invite_post() in /application/controllers/api/User
- The invitation is validated and user_library->member_invitation() creates a link that looks like
Some_Applicant, you have been invited to join timetravelbookings.com by TimeTraveller Please go to timetravelbookings.com to accept or decline the invitation.. Regards from the timetravelbookings team
With a link to: (something like) https://timetravelbookings.com/user/membership/ABC123/?email=some@applicant.com enclosed.
Visitors can request an invitation.
Say a visitor is utterly amazed by the site, but doesn't know any users:
- Visitor clicks the sign in button
- A link, at the bottom of the sign in form, says "Request an invitation"
- The "register" dialog, asking for an email, and a reason for the request calls api/user/request_invitation
- in User.php, function request_invitation() calls user_library->notify_invitation_request
- in User_library.php notify_invitation_request() sends an email to admin@... containing the link, below
- (this_site)/user/invite/some.email@etc.com}/SomeApplicantsName
- The administrator, clicking on this line, gets a link saying "Just do it manually for now" (because this hasn't been automated)
- TODO - check if an invitation has already been sent: Job done
- TODO - create a link which automates the invitation
- But we haven't automated this process so the administrator must resort to the standard invitation process, described above.
- Begin the registration process
- Javascript client (in register.js) does pre validation
- Post a username, email and desired password (/application/controllers/api/User/register_post())
- Server validates further (what if the name & email are already in use? TODO!)
- /application/controllers/User.php calls ??
- The registration request is saved as user in the database, with a key?
- A confirmation link is embedded in an email and sent to the user's address
- The user opens the link to function register() in /application/controllers/user.php
- register() marks the user as a bompilgom (pointless invented word)
It wraps the tour description information into a data structure and calls the 'api' function to send it to
"tour/description", this gets converted to a post call to the description_post function in /Controller/api/tour.php
It wraps the tour description information into a data structure and calls the 'api' function to send it to
"tour/description", this gets converted to a post call to the description_post function in /Controller/api/tour.php
It doesn't work in Internet Explorer, although we lament it's loss.
Phones, once known as "cell phones" or "mobile phones", or the smart variety, tend to support modern browsers.
When a browser client (Chrome, Edge etc) attempts to render html, it uses cascading stylesheets (css) to determine how to layout the page, colour backgrounds, etc. If the browser comes across an instruction that it doesn't understand, it simply ignores it, so this can be used as a fallback mechanism.
.gray-element { color: #ccc; /* definitely understands this */ color: rgba(0, 0, 0, 0.5); /* if it understand's this, ignore above, otherwise ignore this */ } @supports(display:grid) { .some-layout-class{ width: auto; } }
- First impressions: How the site appears to the casual user - load and browse:
Are the images displayed? Do the links work? - Try to join: Is the process clear? Does the candidate receive a confirmation email?
- Do the administrators receive an email (for them to clear and forward an invitation)?
- Does the candidate receive the invitation?
- Does the link in the invitation register and sign them on?
- User impressions:
- Responsive design: Does it resize and work well on mobile devices?
- Do the dialogs come into view and display correctly?
- Do the links work?
- Home page:
- Timeline
- Quickbook
- Timeline:
- Zooming and panning
- Event dialog
- Tours:
- List
- Search
- Create a tour
- Create tour:
- List
- Search
- Create a tour
- Static pages:
- How it works
- The science
- Forum:
These are the some common errors:
On the client, javascript errors can be silent. For example, the user clicks a button and nothing happens.
- Is there a console message? Open the browser's development tools and look
for the javascript console.
Sometimes the button's (or other element's) handler was never attached because an earlier error in the javascript halted execution beforehand.
For example, in a booking page, does Book exist? You can just enterBook
in console and it might say "undefined". The main javascript objects are listed here
Query errors are uncommon because they tend to be quickly exposed in testing
Example mysql query error:A Database Error Occurred
Error Number: 1064
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'count(b.id) as booking_count from tour t join user u on t.created_by = u.id left' at line 1
select t.id as tour_id, t.title as tour_title, t.description, substring(t.description, 1, 64) 'shortdesc', t.themes, u.username, m.kind, m.url as image_url,
... etc ...
t.created_by='4257c5de3ea190f' and visibility >= 0 group by t.id, m.kind, m.url, m.copyright, u.id
Filename: models/Tours_model.php
Line Number: 270
-
There are many
log_message('debug', "some message");statements dotted through the code. It is located in /system/core/Common.php (v 3.x) and writes daily files located in /application/logs.
If no log files are generated then it is probably because the file path needs read/write/execute privileges. /application/config/config.php
Great advice that we wish we'd had before we learned by failing, here.
Image cropping here
- Where needed image containers are built into templates and html like this:
- When the document is loaded by the browser, javascript invokes uploader.js to
attach a handler to a change in the >input type="file" ...< element.
(see uploader.js) - On the server, in controller/application/Upload.php method up() attempts to receive the image, subject to constraints (size, media).
- up() then calculates the sha hash of the image and looks it up in the media table to determine whether the same file has been uploaded previously.
- If it exists then we simply return the image's id and url to the client.
- If it doesn't exist (note the hash would produce different values for the same image that has been resized beforehand) we process the image (creating a smaller version and a thumb) assign an media_id to it and return that id along with the image url and the hash value.
- The user could abandon their work at this stage and the file would remain, orphaned, on the server, so in Controllers/Upload/up() we also create a record in the media table.
log_message('debug', "some message");
$data[ '_server_js' ][ 'some_key' ] = $some_data; $data[ 'TPL' ] = hpr_load_templates_dev($this, $data, $array_of_templates);the contents of _server_js is converted into useful client readable data before it is downloaded, by writing in the view (/application/views or /application/view/templates):
<script type="text/javascript"> <?php if ( isset( $_server_js ) ) { ?> var Srv = <?php echo json_encode( $_server_js ); ?> console.log("Server.page:" + Srv.page); <?php } ?> </script>which unwinds to
<script type="text/javascript"> var Srv = {"page":"tech-notes"}; console.log("Server.page:" + Srv.page); </script>
When form data is posted to the server by calling Api.post(method, callbacks...), the appropriate controller in /application/api is called
Api.post('book/add_traveler')resolves to /application/api/book.php calling method add_traveler_post()
If an error is returned from the function, via hpr_rest_error(....) then Api calls the error call-back, which usually, in turn, calls
Dialog.showErrors(errors).
errors is a json data structure built on the server and contains key-data pairs, viz: error-class -> error_message
showErrors() makes any element with class error-class visible and sets the text in that element to error_message then scrolls to the first error, if it's not already in view.
This a list of the main scripts and their objects (all located in assets/js)
They can be downloaded directly as, for example, in the browser:
https://timetravelbookings.com/assets/js/dialog.jsand then you can chuckle away at our amateur enthusiasm. (We mean well)
- dialog.js -> Dialog: handles the display of messages confirmations and other dialogs
- home.js -> Home: mostly used in validating and posting the quick-book section.
- tour.js -> Tour: displays tour data found in the server-generated Srv javascript object, and captures user input
- picker.js -> Picker: special purposed dialogs to allow the user to pick locations and dates while creating tours
- uploader.js -> Uploader: for image uploads (see notes on images)
- book.js -> Book: works with Tour to display Srv.pending_book data and capture
booking information. Note that a booking is a tour with a departure location/date
and an optional return location/date, plus travelers.
Consequently Book is not interested in the first destination or any subsequent trips.
All these are handled by Tour.
- location.js -> Location: provides a lookup system for the first departure location
- traveler.js -> Traveler: loaded during the traveler capture step of a booking
- events.js -> Events manages the timeline, and is not named "Timeline.js" as this class is taken by /applications/timeline.js
- forum.js -> Forum
- static.js performs some basic navigation function on the Science and Faq pages
Note handling anchor is explained here
- register.js -> Register is unusual because registration is