Say Hello to a Borderless World!
Borderless is a platform to share your adventures with an enthusiastic community of adventurers. Your best moments worth be a memory, BorderLess is a platform to record your travels, tracks or any adventure around the world. Be without limits, Be borderless, enjoy a whole world with breathtaking landscapes, different colors, cultures and share with your friends with your notes, advices and suggestions.
Click here to see the project live
- Borderless
- Tables of Contents
- UX
- Design Process:
- Features
- Technologies Used
- Testing
- Accessibility
- Deployment
- Cloning my project
- Credits
- Share your best moments in travels, short trips, vacation, hikings and so on.
- Share your opnion about a place, with suggestions, advices and photos.
- Encourage interaction between users with the the relationship and likes functionality.
- Create a resposive design, wich works in different screen sizes and devices, due the fact that people use more mobile devices nowadays.
"As a user, I would like to _____________________________"
- view the site from any device (mobile, tablet, desktop).
- view all trip posts as a guest.
- view all trip as a guest.
- view public profile as a guest.
- search trip posts by trip name and place.
- create my own profile.
- choose my trip post privacy.
- edit my own profile.
- add my own trips.
- edit my own trips.
- delete my own trips.
- be able to like and dislike a trips.
- be able to follow or unfollow other users.
- be able to receive a notification when another user send a follow request.
- be able to to remove a follower.
- be able see my followers and following users in my profile.
- be able see a statistic about my trips in my profile.
- be able to log out.
Back to the Tables of Contents
- Bootstrap:
- Bootstrap is a responsive mobile-first design framework with a clean and modern layout, with its simple-to-understand documentation.
- Jquery:
- In an effort to keep the JavaScript minimal, I have decided to use jQuery as foundation to my scripts framework.
- Flask:
- Flask is a microframework that I've used to render the back-end Python with the front-end Bootstrap.
-
- Software used to build the wireframe of this project. Because Code Institute have provided all students with free access for a limited time and the simplicity and ease of use.
-
The wireframe can be seen here:
Back to the Tables of Contents
-
This project has nine separate pages, which are:
- "Feed" page (which is the first page), where the user can see all public trips posted;
- "About" page which contains a brief information about the platform with a button to open the sign up modal form;
- "Profile" page where the user can see and manipulate your own trips, profile or relationships (followers and following);
- "Public Profile" page where the user can see others user's profile and send a relationship request (follow/unfollow);
- "Edit Profile" page where the user can find a form to update his profile;
- "Edit Trip" page where the user can find a form to update a trip;
- "Trip" page where the user can see all informations about a trip.
- 404 and 500 error page to handle these erros.
-
Register Account:
- Anybody can register for free and create their own unique account. I have built-in authentication and authorization to check certain criteria is met before an account is validated. All passwords are hashed for security purposes. When an user is registered in the database, a folder for this user is automaticly created in the cloud platform to hold all pictures of his account. (see cloud folders diagram when user is registered)
-
Log In to Account:
-
Log Out of Account:
-
Navbar:
-
The navbar is different, depending whether the user is logged in or not. If the user is not logged in, the logo will be the brand logo picture with the brand name, and the items will be "About" (Which redirects to about page), "Sign Up" (Which open a sign up form) and "Login" (Which open a log in form). If the user is logged in the logo will be just the brand logo picture, and the items will be a globe icon (which redirects to feed page), user's avatar (Which is a dropdown menu with the option to visit profile, edit profile and log out), a bell icon (Which is a dropdown menu where the user can find his notifications), if there is notification to the user, the bell icon will be solid, with there is no notification to the user, the bell icon will be outlined.
-
-
View All Trip Posts:
- On the feed page, all trip posts are initially displayed in an date order, being ordered by the latest initial trip date to oldest trip date, with a standard 10-items per page using pagination.
-
Search Trips:
-
Add a Trip:
- Create or 'add' a new trip. The 'Add New Trip' button can be found in the profile page. Defensive programming in place means users must adhere to minimal requirements when adding a new trip like fill required fields and limit the number of photos will be posted. If a user doesn't have a photo to accompany their trip, I have a built-in function that will automatically assign a placeholder image. A folder named by the trip category is created in the cloud platform to hold a folder named by the trip name which contains the trip pictures. (see trips folder diagram)
-
Trip Post:
-
Trip post can be found in the profile, public profile and feed pages. It's a link to the trip view and trip author's public profile, with a minimal information about the trip. From here, users also have additional options:
- Delete the trip:
- Delete trip from database and trip folder in the cloud platform, with a defensive message to avoid accidental deletion. All pictures are moved to the delete folder in the cloud which is a stage before complete deletion;
- Edit the trip:
- Allow user to update all informations about the trip in the data base and in the cloud platform, in the edit trip form there is section to se the current photos and allow user to delete any of the current photos on click (with a defensive message), there is a section to preview the new photos that will be uploaded. Depending on whether the user change the trip name, category or both, the trip photo will be moved to another new folder in the cloud;
- Like/Unlike the trip:
- Allow user to like or deslike the trip post. Demonstrates the popularity of the post and can be an indicator of the post quality;
- Access Trip View:
- Access trip view through trip's name on the post, which is a link;
- Access Author Public Profile:
- Access trip's author clicking in the author's avatar picture on the post
- Delete the trip:
-
-
Trip View:
-
Edit Profile:
- Allow user to update profile Name and choose a new avatar and cover picture, beside the file input for choose the new avatar and cover picture there is a preview of the current avatar and cover picture. If the user choose new pictures to the profile, the old pictures will be overwriten in cloud folder.
-
Followers Tab:
-
Following Tab:
-
Statistics Tab:
-
Follow/Unfollow Button:
Back to the Tables of Contents
-
Add comments to the trip posts.
-
Add the option to create a list of favorite trips.
-
Change pagination to infinite scroll with lazy loading.
Back to the Tables of Contents
-
- Used as my primary IDE for coding. Gitpod is an open source platform for automated and ready-to-code development environments that blends into your existing workflow directly from your browser.
-
- Used as remote storage of my code online. A company that provides hosting for software development version control using Git.
-
- Used as a basic template to kick start the project.
-
- Used for wireframing the project. see wireframe
-
- Used to create the database schema. See data base schema
-
- Used to build cloud folders diagrams. see main diagram / see trips diagram
-
- A set of web developer tools built directly into the Google Chrome browser. I used these tools constantly thoughout the development cycle.
-
- I used the Canva platform to make the style guide.
-
- I used TinyPNG to compress my image files to try to reduce the loading time for each page.
-
- Great tool to support throught the web development that helps to test and find issues on markup file.
-
- Great tool to support throught the web development that helps to test and find issues on style file.
-
- W3C Schools is a great platform that covers all aspects of web development, great tools that provides information for developers.
-
- Although it isn't a technology, I found a lot of guidance on Stack Overflow.
-
- DN Web Docs, previously Mozilla Developer Network and formerly Mozilla Developer Center, is a documentation repository for web developers used by Mozilla, Microsoft, Google, and Samsung.
-
- Is a blog where you can find lots of tutorial and tricks to write a good css.
Back to the Tables of Contents
-
- Used as the base for markup text. The language used to build the structure and add its content.
-
- Used as the base for cascading styles. The language used to style the HTML5 elements according to the design and color scheme.
-
- Used as the overall design framework. I decided to use Bootstrap's grid container system as I wanted to design my project with a 'mobile first' approach, but another bootstrap resources were used like the contact form.
-
- FontAwesome icons is where I got most part of the icons for my design project.
-
- I used Google fonts to provide the fonts of the website.
-
- Used as the primary JavaScript functionality. In an effort to keep the JavaScript minimal, I have decided to use jQuery as foundation to my scripts framework.
Back to the Tables of Contents
-
- Used as a microframework.
-
- Used to split the python code for routes.
-
- Used for templating with Flask.
-
- Used for password hashing, authentication, and authorization.
-
- Used to store users's pictures
-
- Used for app hosting.
-
- Used as the back-end programming language.
-
- Used to store my database in the 'cloud'.
-
- Used as the Python API for MongoDB.
Back to the Tables of Contents
"As a user, I would like to _____________________________"
-
view the site from any device (mobile, tablet, desktop).
-
view all trip posts as a guest.
-
view all trip as a guest.
-
view public profile as a guest.
-
create my own profile. I've created my own personal account. In addition to this primary account, I've tested with about 5 fake accounts in order to confirm authentication and validation worked as expected. The authentication and validation to create an account consists in:
-
Check if the Sign up form is properly filled out.
-
If the "First name" and "Last name" fields are filled out, with a minimum length of 2 characters and maximum of 15 characters;
<!-- First Name Field --> <div class="row no-gutters content-alignment"> <div class="col-8"> <label for="fname">First Name</label> <input type="text" id="fname" name="fname" minlength="5" maxlenght="15" pattern="^[a-zA-Z0-9]{2,15}$" class="form-control" placeholder="First name" required> </div> </div> <!-- Last Name Field --> <div class="row no-gutters content-alignment"> <div class="col-8"> <label for="lname">Last Name</label> <input type="text" id="lname" name="lname" minlength="5" maxlenght="15" pattern="^[a-zA-Z0-9]{2,15}$" class="form-control" placeholder="Last name" required> </div> </div>
-
If the "Email" field is filled out with the properly email format;
<!-- Email Field --> <div class="row no-gutters content-alignment"> <div class="col-8"> <label for="email">Email</label> <input type="email" id="email" name="email" class="form-control" placeholder="Email" required> </div> </div>
-
If the "Password" and "Confirm Password" field are properly filled out with a minimum length of 6 characters and maximum of 15 characters and if these fields match, in case of these fields not match, the register button "Register" keep disabled.
<!-- Password Field --> <div class="row no-gutters content-alignment"> <div class="col-8"> <label for="password">Password</label> <input type="password" id="password" name="password" minlenght="6" maxlenght="15" pattern="^[a-zA-Z0-9]{5,15}$" class="form-control" placeholder="Password" required> </div> </div> <!-- Repeat Password Field --> <div class="row no-gutters content-alignment"> <div class="col-8"> <label for="Rpassword">Confirm Password</label> <input type="password" id="Rpassword" name="Rpassword" minlenght="6" maxlenght="15" pattern="^[a-zA-Z0-9]{5,15}$" class="form-control" placeholder="Password" required> </div> </div>
-
-
Check if the user already exists in the database, using the email as parameter, if the user already exists, the return to the previous page with a flash message which says "User already exists";
# Check if the username already exists in db existing_user = users_collections.find_one( {"email": request.form.get("email")}) if existing_user: flash("User already exists") # redirect to previous page if the users already exists return redirect(request.referrer)
-
-
add my own trips.
-
edit my own trips.
-
delete my own trips. Several trip posts were created in order to prepare for pagination building. These trip posts were created using my actual account, the admin account, and several test accounts. For several posts, I've edited minor things to test all parts of a trip post, adding place and country, description, photos and creating without photos to test the functionality of creating and updating a trip post to the database and cloud platform.
-
be able to receive a notification when another user send a follow request.
I've tested the log in form and functionality in all initial pages, filling the form with the correct values and different values to try to catch any unexpected error, authentication and validation worked as expected. The authentication and validation to log in consists in:
-
Check if the Log in form is properly filled out.
-
Check if the user exists in the database, using the email as parameter, in the case the user exists in the database, then check if the password match with the password stored in the database. If the email or password is incorrect, then reload the previous page with a flash message, which is "Incorrect email and/or password";
When implementing pagination, I had a lot of manual tests to undergo, in order to make pagination work for multiple scenarios. I needed to test that all aspects of pagination worked with and without the option for searching the database. These included:
- Pagination without Search
- Pagination works if no URL args present on initial load;
- Previous Page button disabled on first page of trips shown;
- Next Page button disabled on last page of all trips shown;
- Appropriate number of pages appear based on the number of 10 items per page in the feed page or 5 items in the profile and public profile pages with correct number of trips in database;
- Additional page numbers appear if more trips are added;
- Each page number returns their correct page URL.
- Pagination with Search
- Pagination works if no URL args present on initial search;
- Previous Page button disabled on first page of search results;
- Next Page button disabled on last page of search results;
- Additional page numbers appear if more trips are added.
- Each page number returns their correct page URL.
I've checked the trip post in different pages (Profile, Public Profile and Feed). Each user is just allowed to modify or delete your own trip posts, in this case the delete trip button and edit trip button need to be hidden in other users trip posts. The like button can't be seen with the user is logged out.
All pages checked. All errors and warnings fixed.
- W3C HTML Validator - In the first Check some erros and warning were found, like:
- Legend element can't be inside a div, referent to Sign up and Log in form modals. The legend elements in the Sign up and Log in modals were changed to h3 elements;
- Were found duplicated IDs (startDate, endDate, totalDays and totalPhotos) in the trip posts, referent to trip post template (_post.html), which is used in a loop and it generates the duplicated IDs error. For each id was added the 'trip id' from the data base, then each id is different in each post. Exemple:
-
Before :
<span id="startDate">{{ trip['trip_startdate'].strftime('%Y-%m-%d') }}</span>)
It results in duplicated ID's when the database is iterated and the trip posts are rendered.
-
After :
<span id="startDate_{{ trip._id }}">{{ trip['trip_startdate'].strftime('%Y-%m-%d') }}</span>)
It results in different ID's when the database is iterated and the trip posts are rendered.
-
In the second check was found a typo error, in the maxlength and minlength attribute of some input elements. This error was fixed.
- W3C CSS Validator - No error found in the style sheet.
-
JShint - "Metrics: There are 20 functions in this file. Function with the Largest function has 13 statements in it, while the median is 1. The most complex function has a cyclomatic complexity value of 5 while the median is 1."
- "'arrow function syntax (=>)' is only available in ES6 (use 'esversion: 6').";
- "'let' is available in ES6 (use 'esversion: 6') or Mozilla JS extensions (use moz).";
- "'const' is available in ES6 (use 'esversion: 6') or Mozilla JS extensions (use moz).";
- "'template literal syntax' is only available in ES6 (use 'esversion: 6')."
- This code works well despite these warnings
- '$' - This is for jQuery, is a sign to define/access jQuery;
- 'modal' - Which is a function to handle the modals;
-
JSesprima - "Code is syntactically valid."
I manually tested the website on the following web browsers, checking buttons, responsiveness and design worked as planned:
- Google Chrome
- Mozilla Firefox
- Microsoft Edge
-
The Unfollow button in public profile page, when clicked all code related to remove user from the specific field in the database works well and the button UI turn to a Follow Button, but it does not behave as a Follow button unless the page is reloaded.
-
In case an user is logged with two pages opened in different tabs and one of theses pages is the profile page, if the user log out in the tab that isn't the profile and after try to reload the profile page, then he would see a KeyError error, because there is no longer any session called 'user'. This error was fixed adding a try/except statement in every page that needs a user logged to be used, if there is no longer session 'user', then redirect to feed page, which does not need the session 'user'.
-
If a user create a trip where the place name does not exists in the country, the google maps is not rendered.
-
Unfollow link in Following tab (profile page) and Remove Follower in the Followers tab (profile page), just work with a double click.
-
Icons from google maps are not rendering.Fixed adding 'data:' data scheme to content security policy. -
Pictures in preview photos in edit trip page are not rederingFixed adding 'blob:' data scheme to content security policy
Back to the Tables of Contents
- Each image has an alt attribute describing the image's function
- Each anchor tag has an aria-label attribute describing where that link goes
- Each section has an aria-labelledby attribute
- Content has a contrast with the background to improve the visibility
- Form inputs have labels
Back to the Tables of Contents
Please note - in order to run this project locally on your own system, you will need the following installed:
- Python3 to run the application.
- PIP to install all app requirements.
- Any IDE such as Microsoft Visual Studio Code.
- GIT for cloning and version control.
- MongoDb to develop your own database either locally or remotely on MongoDB Atlas.
Next, there's a series of steps to take in order to proceed with local deployment:
-
Clone this GitHub repository by either clicking the green Clone or download button and downloading the project as a zip-file (remember to unzip it first), or by entering the following into the Git CLI terminal:
-
Navigate to the correct file location after unpacking the files.
-
cd
-
-
Create a .env file with your credentials. An example can be found here. Be sure to include your MONGO_URI and SECRET_KEY values.
-
Create a .flaskenv file and add the following entries:
-
FLASK_APP=run.py
-
FLASK_ENV=development
-
-
Install all requirements from the requirements.txt file using this command:
-
sudo -H pip3 -r requirements.txt
-
-
Sign up for a free account on MongoDB and create a new Database called 2BN-Desserts. The Collections in that database should be as in the schema:
-
You should now be able to launch your app using the following command in your terminal:
-
flask run
-
The app should now be running on localhost on an address similar to http://10.116.8.16:8000/. Simply copy/paste this into the browser of your choice!
Back to the Tables of Contents
This site is currently deployed on Heroku using the master branch on GitHub. To implement this project on Heroku, the following steps were taken:
-
Create a requirements.txt file so Heroku can install the required dependencies to run the app.
-
pip3 freeze --local > requirements.txt
- My file can be found here.
-
-
Create a Procfile to tell Heroku what type of application is being deployed, and how to run it.
-
echo web: python run.py > Procfile
- My file can be found here.
-
-
Sign up for a free Heroku account, create your project app, and click the Deploy tab, at which point you can Connect GitHub as the Deployment Method, and select Enable Automatic Deployment.
-
In the Heroku Settings tab, click on the Reveal Config Vars button to configure environmental variables as follows:
- IP: 0.0.0.0
- PORT: 5000
- MONGO_URI: <database_uri>
- MONGO_DBNAME: <database_name>
- SECRET_KEY: <your_own_secret_key>
- MAP_KEY: <Google_maps_API_key>
- CLOUDINARY_API_KEY: <Cloudinary_API_key>
- CLOUDINARY_API_SECRET: <Cloudinary_secret>
- CLOUDINARY_CLOUD_NAME: <Cloudinary_cloud_name>
-
Your app should be successfully deployed to Heroku at this point.
If you would like to work on my project further you can clone it to your local machine using the following steps:
- Scroll to the top of my repository and click on the "clone or download button"
- Decide whether you want to clone the project using HTTPS or an SSH key and do the following: * HTTPS: click on the checklist icon to the right of the URL * SSH key: first click on 'Use SSH' then click on the same icon as above
- Open the 'Terminal'
- Change the current working directory to the location where you want the cloned directory
- Type 'git clone', and then paste the URL you copied earlier.
- Press 'Enter' to create your local clone.
You can find both the source of this information and learn more about the process on the following link: Cloning a Repository
Back to the Tables of Contents
Thank you to the following people who helped with support, inspiration and guidance at different stages in the project:
- My mentor Caleb Mbakwe
- Tim Nelson whom I inspired to organize the project and the README file.
- Code Institute Mentors and Tutors.
- Code Institute Student Care, which is always Kind.
- My class on slack.
- The supportive Code Institute community on Slack.
- My family and friends for their patience and honest critique throughout.