aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.idea/misc.xml13
-rw-r--r--README.md11
-rw-r--r--app.js9
-rw-r--r--dbTests/exams_data.json (renamed from test/dbTests/exams_data.json)0
-rw-r--r--dbTests/method.txt81
-rw-r--r--dbTests/node_simple_backup.js1458
-rw-r--r--dbTests/test_main.js (renamed from test/dbTests/test_main.js)0
-rw-r--r--dbTests/testingDocs.txt (renamed from test/dbTests/testingDocs.txt)0
-rw-r--r--dbTests/users_data.json (renamed from test/dbTests/users_data.json)0
-rw-r--r--node_simple.js10
-rw-r--r--routes/index.js10
-rw-r--r--routes/user.js6
-rw-r--r--test/dbTests/resulst.txt78
-rw-r--r--test/routingDb_tests.js22
-rw-r--r--test/test_server.js2
-rw-r--r--views/partials/footer.hbs4
-rw-r--r--views/privacy_policy.hbs12
-rw-r--r--views/terms_and_conditions.hbs63
18 files changed, 1659 insertions, 120 deletions
diff --git a/.idea/misc.xml b/.idea/misc.xml
deleted file mode 100644
index 72abef0..0000000
--- a/.idea/misc.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project version="4">
- <component name="ProjectLevelVcsManager" settingsEditedManually="false">
- <OptionsSetting value="true" id="Add" />
- <OptionsSetting value="true" id="Remove" />
- <OptionsSetting value="true" id="Checkout" />
- <OptionsSetting value="true" id="Update" />
- <OptionsSetting value="true" id="Status" />
- <OptionsSetting value="true" id="Edit" />
- <ConfirmationsSetting value="0" id="Add" />
- <ConfirmationsSetting value="0" id="Remove" />
- </component>
-</project> \ No newline at end of file
diff --git a/README.md b/README.md
index 70cb5cb..2dca503 100644
--- a/README.md
+++ b/README.md
@@ -50,9 +50,7 @@ Usage and features:
Signed In User:
- In order to sign in and take advantage of commenting and adding solutions please click the signup button at the
- top of any of the pages. Then fill out the forms and press submit. Once signed in you will be able to enjoy
- all the features of a general user + the ones mentioned below
+ In order to sign in and take advantage of commenting and adding solutions you must sign in via your facebook account.
Examination + Solution Search:
Add Solution:
@@ -71,13 +69,14 @@ Usage and features:
you have in the "Friends" tab. You can also see the messages you have received in "Inbox" and the comments
you have made in "Comments". Please follow the following steps to friend a user, message a user or follow a
user:
+ Follow an exam:
- Follow a user:
-
- Friend a user:
+ 1. Once signed in search for the course you wish to see the exam for and select and exam of your choice
+ 2. Click the "Follow Exam" button once in the page displaying the list of questions for an exam
Message a user:
+
1. Once signed in click on the "PROFILE PAGE" at the top of any of the pages
2. Click "Send Message"
3. Fill in the forms: "Send To:" must contain the username of the person you are trying to reach
diff --git a/app.js b/app.js
index 8d97dc8..4ef362e 100644
--- a/app.js
+++ b/app.js
@@ -77,7 +77,7 @@ app.use('/', routes);
/* Handle error page */
app.use(function(req, res, next){
- res.status(404 || 500);
+ res.status(404);
// respond with html page
if (req.accepts('html')) {
@@ -90,5 +90,12 @@ app.use(function(req, res, next){
});
+app.use(function(error, req, res, next) {
+ res.status(500);
+ url = req.url;
+ res.render('error', {error: error, url: url});
+});
+
+
module.exports = app;
diff --git a/test/dbTests/exams_data.json b/dbTests/exams_data.json
index 493bbf5..493bbf5 100644
--- a/test/dbTests/exams_data.json
+++ b/dbTests/exams_data.json
diff --git a/dbTests/method.txt b/dbTests/method.txt
new file mode 100644
index 0000000..0d02e14
--- /dev/null
+++ b/dbTests/method.txt
@@ -0,0 +1,81 @@
+When we first built the database driver, we tried to be as modular as possible. Each query was supposed to be as independent as possible so that if it had flaws, it wouldnt affect any of the others. In this process, we made each function that interacted with the database create its own seperate connection (node_simple_backup.js).
+
+Unfortunately there isnt much support (framework wise) for testing the mongodb. So we created a simple program to try the main features (test_main.js). We used an online json generator to create users and exmas (testingDocs.txt). In the testing phase we tried to add a set of 50 users, we saw the following in the mongod log file:
+
+ Wed Jul 27 10:42:09.669 [initandlisten] connection accepted from 127.0.0.1:44510 #20 (2 connections now open)
+ Wed Jul 27 10:42:09.670 [initandlisten] connection accepted from 127.0.0.1:44511 #21 (3 connections now open)
+ Wed Jul 27 10:42:09.670 [initandlisten] connection accepted from 127.0.0.1:44512 #22 (4 connections now open)
+ Wed Jul 27 10:42:09.670 [initandlisten] connection accepted from 127.0.0.1:44513 #23 (5 connections now open)
+ Wed Jul 27 10:42:09.670 [initandlisten] connection accepted from 127.0.0.1:44514 #24 (6 connections now open)
+ Wed Jul 27 10:42:09.670 [initandlisten] connection accepted from 127.0.0.1:44515 #25 (7 connections now open)
+ Wed Jul 27 10:42:09.670 [initandlisten] connection accepted from 127.0.0.1:44516 #26 (8 connections now open)
+ Wed Jul 27 10:42:09.670 [initandlisten] connection accepted from 127.0.0.1:44517 #27 (9 connections now open)
+ Wed Jul 27 10:42:09.670 [initandlisten] connection accepted from 127.0.0.1:44518 #28 (10 connections now open)
+ Wed Jul 27 10:42:09.670 [initandlisten] connection accepted from 127.0.0.1:44519 #29 (11 connections now open)
+ Wed Jul 27 10:42:09.670 [initandlisten] connection accepted from 127.0.0.1:44520 #30 (12 connections now open)
+ Wed Jul 27 10:42:09.670 [initandlisten] connection accepted from 127.0.0.1:44521 #31 (13 connections now open)
+ Wed Jul 27 10:42:09.670 [initandlisten] connection accepted from 127.0.0.1:44522 #32 (14 connections now open)
+ Wed Jul 27 10:42:09.670 [initandlisten] connection accepted from 127.0.0.1:44523 #33 (15 connections now open)
+ Wed Jul 27 10:42:09.670 [initandlisten] connection accepted from 127.0.0.1:44524 #34 (16 connections now open)
+ Wed Jul 27 10:42:09.670 [initandlisten] connection accepted from 127.0.0.1:44525 #35 (17 connections now open)
+ Wed Jul 27 10:42:09.670 [initandlisten] connection accepted from 127.0.0.1:44526 #36 (18 connections now open)
+ Wed Jul 27 10:42:09.670 [initandlisten] connection accepted from 127.0.0.1:44527 #37 (19 connections now open)
+ Wed Jul 27 10:42:09.670 [initandlisten] connection accepted from 127.0.0.1:44528 #38 (20 connections now open)
+ Wed Jul 27 10:42:09.670 [initandlisten] connection accepted from 127.0.0.1:44529 #39 (21 connections now open)
+ Wed Jul 27 10:42:09.670 [initandlisten] connection accepted from 127.0.0.1:44530 #40 (22 connections now open)
+ Wed Jul 27 10:42:09.670 [initandlisten] connection accepted from 127.0.0.1:44531 #41 (23 connections now open)
+ Wed Jul 27 10:42:09.670 [initandlisten] connection accepted from 127.0.0.1:44532 #42 (24 connections now open)
+ Wed Jul 27 10:42:09.670 [initandlisten] connection accepted from 127.0.0.1:44533 #43 (25 connections now open)
+ Wed Jul 27 10:42:09.670 [initandlisten] connection accepted from 127.0.0.1:44534 #44 (26 connections now open)
+ Wed Jul 27 10:42:09.670 [initandlisten] connection accepted from 127.0.0.1:44535 #45 (27 connections now open)
+ Wed Jul 27 10:42:09.670 [initandlisten] connection accepted from 127.0.0.1:44536 #46 (28 connections now open)
+ Wed Jul 27 10:42:09.671 [initandlisten] connection accepted from 127.0.0.1:44537 #47 (29 connections now open)
+ Wed Jul 27 10:42:09.671 [initandlisten] connection accepted from 127.0.0.1:44538 #48 (30 connections now open)
+ Wed Jul 27 10:42:09.671 [initandlisten] connection accepted from 127.0.0.1:44539 #49 (31 connections now open)
+ Wed Jul 27 10:42:09.671 [initandlisten] connection accepted from 127.0.0.1:44540 #50 (32 connections now open)
+ Wed Jul 27 10:42:09.671 [initandlisten] connection accepted from 127.0.0.1:44541 #51 (33 connections now open)
+ Wed Jul 27 10:42:09.671 [initandlisten] connection accepted from 127.0.0.1:44542 #52 (34 connections now open)
+ Wed Jul 27 10:42:09.671 [initandlisten] connection accepted from 127.0.0.1:44543 #53 (35 connections now open)
+ Wed Jul 27 10:42:09.671 [initandlisten] connection accepted from 127.0.0.1:44544 #54 (36 connections now open)
+ Wed Jul 27 10:42:09.671 [initandlisten] connection accepted from 127.0.0.1:44545 #55 (37 connections now open)
+ Wed Jul 27 10:42:09.671 [initandlisten] connection accepted from 127.0.0.1:44546 #56 (38 connections now open)
+ Wed Jul 27 10:42:09.671 [initandlisten] connection accepted from 127.0.0.1:44547 #57 (39 connections now open)
+ Wed Jul 27 10:42:09.671 [initandlisten] connection accepted from 127.0.0.1:44548 #58 (40 connections now open)
+ Wed Jul 27 10:42:09.671 [initandlisten] connection accepted from 127.0.0.1:44549 #59 (41 connections now open)
+ Wed Jul 27 10:42:09.671 [initandlisten] connection accepted from 127.0.0.1:44550 #60 (42 connections now open)
+ Wed Jul 27 10:42:09.671 [initandlisten] connection accepted from 127.0.0.1:44551 #61 (43 connections now open)
+ Wed Jul 27 10:42:09.671 [initandlisten] connection accepted from 127.0.0.1:44552 #62 (44 connections now open)
+ Wed Jul 27 10:42:09.671 [initandlisten] connection accepted from 127.0.0.1:44553 #63 (45 connections now open)
+ Wed Jul 27 10:42:09.671 [initandlisten] connection accepted from 127.0.0.1:44554 #64 (46 connections now open)
+ Wed Jul 27 10:42:09.671 [initandlisten] connection accepted from 127.0.0.1:44555 #65 (47 connections now open)
+ Wed Jul 27 10:42:09.671 [initandlisten] connection accepted from 127.0.0.1:44556 #66 (48 connections now open)
+ Wed Jul 27 10:42:09.671 [initandlisten] connection accepted from 127.0.0.1:44557 #67 (49 connections now open)
+ Wed Jul 27 10:42:09.671 [initandlisten] connection accepted from 127.0.0.1:44558 #68 (50 connections now open)
+ Wed Jul 27 10:42:09.672 [initandlisten] connection accepted from 127.0.0.1:44559 #69 (51 connections now open)
+ Wed Jul 27 10:42:09.748 [initandlisten] connection accepted from 127.0.0.1:44560 #70 (52 connections now open)
+ Wed Jul 27 10:42:09.749 [initandlisten] connection accepted from 127.0.0.1:44561 #71 (53 connections now open)
+ Wed Jul 27 10:42:09.751 [initandlisten] connection accepted from 127.0.0.1:44562 #72 (54 connections now open)
+ Wed Jul 27 10:42:09.755 [initandlisten] connection accepted from 127.0.0.1:44563 #73 (55 connections now open)
+ Wed Jul 27 10:42:09.783 [conn71] build index db.users { _id: 1 }
+ Wed Jul 27 10:42:09.783 [conn71] build index done. scanned 0 total records. 0 secs
+ Wed Jul 27 10:42:09.795 [conn72] build index db.logins { _id: 1 }
+ Wed Jul 27 10:42:09.795 [conn72] build index done. scanned 0 total records. 0 secs
+
+
+Notice that the number of connections opened during this operation is 55. This is not at all 'scalable'.
+After this we decided to change our approach. Instead of having each function create its own connection, we could initialize a connection once at the start of the app, and reuse it in each function. We added the function 'setupDB()' which initialized a global 'db' variable which can be accessed in any function in that file (node_simple.js).
+
+After running the same test again we notice:
+
+ Wed Jul 27 10:46:50.430 [initandlisten] connection accepted from 127.0.0.1:44615 #12 (1 connection now open)
+ Wed Jul 27 10:46:50.464 [initandlisten] connection accepted from 127.0.0.1:44616 #13 (2 connections now open)
+ Wed Jul 27 10:46:50.468 [initandlisten] connection accepted from 127.0.0.1:44617 #14 (3 connections now open)
+ Wed Jul 27 10:46:50.471 [initandlisten] connection accepted from 127.0.0.1:44618 #15 (4 connections now open)
+ Wed Jul 27 10:46:50.477 [initandlisten] connection accepted from 127.0.0.1:44619 #16 (5 connections now open)
+ Wed Jul 27 10:46:50.522 [conn14] build index db.users { _id: 1 }
+ Wed Jul 27 10:46:50.523 [conn14] build index done. scanned 0 total records. 0 secs
+ Wed Jul 27 10:46:50.539 [conn15] build index db.logins { _id: 1 }
+ Wed Jul 27 10:46:50.539 [conn15] build index done. scanned 0 total records. 0 secs
+
+This time the max # of connections is 5 for 50 users, far lighter on the system.
+Similar results were noted for adding and removing exams. \ No newline at end of file
diff --git a/dbTests/node_simple_backup.js b/dbTests/node_simple_backup.js
new file mode 100644
index 0000000..4b31f7e
--- /dev/null
+++ b/dbTests/node_simple_backup.js
@@ -0,0 +1,1458 @@
+/**
+ * A simple node driver to interact with mongo database
+ */
+
+/**********************
+ **** COLLECTIONS: ****
+ * ********************
+ *
+ * * * ADMINS - Admins login info
+ * {
+ * fname: first name,
+ * lname: last name,
+ * username: admin username,
+ * password: hashed password
+ * }
+ *
+ * * * COURSES - Course info
+ * {
+ * course_code: course code,
+ * title: course title
+ * }
+ *
+ *
+ * * * EXAMS - Exams info
+ * {
+ * course_code: course code,
+ * year: course year,
+ * term: course term,
+ * type: midterm or final,
+ * instructors: array of instructor names,
+ * page_count: the midterm/exam's number of pages,
+ * questions_count: the midterm/exam's number of questions,
+ * questions_list: array containing question objects, {q_id: question #, question: question},
+ * upload_date: date,
+ * uploaded_by: username
+ * }
+ *
+ *
+ * * * LOGINS - Users login info
+ * {
+ * email: user email,
+ * user_name: user username,
+ * password: users hashed password
+ * }
+ *
+ *
+ * * * MAIL - messaging system base
+ * {
+ * sender: the message sender's username,
+ * receiver: the receiver's username,
+ * message: message,
+ * date: date message was sent
+ * }
+ *
+ *
+ * * * SOLUTIONS - exam solutions
+ * {
+ * exam_id: the id of the exam this solution applies to,
+ * q_id: question id,
+ * text: users solution,
+ * votes: votes,
+ * comments: list of comment objects, {text: comment, date: comment date, by: username}
+ * }
+ *
+ * * * USERS
+ * {
+ * email: user email,
+ * username: username,
+ * f_name: first name,
+ * l_name: last name,
+ * university: user's university,
+ * department: user's department,
+ * answered: number of solutions user posted,
+ * messages: user's inbox count,
+ * comments: user comment count,
+ * phone_num: user phone number,
+ * followers: the list of exams that the user follows
+ * }
+ *
+ *
+ * * * VERIFICATIONS - link facebook profile to the user
+ * {
+ * username: user username,
+ * facebookID: facebook profile id,
+ * facebookToken: facebook token,
+ * }
+ *
+ *
+ */
+
+
+var exports = module.exports = {};
+
+const debug_mode = false;
+
+Object.assign = require('object-assign');
+var mongodb = require('mongodb');
+var mongoFactory = require('mongo-factory');
+var ObjectId = require('mongodb').ObjectID;
+var assert = require('assert');
+var _ = require('underscore');
+
+// Standard URI format: mongodb://[dbuser:dbpassword@]host:port/dbname
+// var uri = exports.uri = 'mongodb://general:assignment4@ds057862.mlab.com:57862/solutions_repo';
+
+
+// Keep this for testing on local machine, do not remove. - Humair
+var uri = exports.uri = 'mongodb://localhost:27017/db';
+
+
+/*******************************FUNCTIONS************************************************/
+
+
+
+/************************* USERS **********************************/
+
+/**
+ * This function will remove the user given user_name from the users table
+ * and the logins table. So far.
+ * IF there is a sessions table, we need to remove it from there as well.
+ * We can leave it in the mail table (involves another user).
+ * We can leave it in the solutions table (solution may still be valid).
+ *
+ * @param {string} username: the unique user_name for the user
+ * @param {function} callback: 2 args: (boolean, <string>),
+ * where <boolean> : err ? false : true
+ * where <string> : error ? error_mssg : success_mssg
+ */
+exports.remove_user = function (username, callback) {
+
+ mongoFactory.getConnection(uri).then(function(db) {
+
+ // fetch the exams table
+ var users = db.collection('users');
+ var logins = db.collection('logins');
+
+ // look for the specific user
+ users.removeOne( { user_name: username }, function (err, docs) {
+ if (err) throw err;
+ else if (docs.deletedCount == 1) {
+ // now remove it from logins ....
+ logins.removeOne( { user_name: username }, function (err, result) {
+ if (err) throw err;
+ else if (result.deletedCount == 1) {
+ callback(true, "User was removed successfully from both tables");
+
+ }
+ });
+ }
+ else if (docs.deletedCount == 0) {
+ callback(false, "No such user was found");
+
+ }
+ });
+ }).catch(function(err) {
+ console.error(err);
+ });
+};
+
+/**
+ * This function will search through the users table to look for 'token'.
+ * It will search the user_name, f_name, l_name fields of the table.
+ * Do not worry about case sensitivity. Malicious string is a possibility though.
+ * Returns a [] of user object(s)
+ *
+ * @params {string} token: search term (hopefully user info)
+ * @params {function} callback: with 2 args: (boolean, <string>),
+ * where <boolean> : err ? false : true
+ * where <string> can be error message
+ * OR on success <[Objs]> RESULT
+ * */
+exports.search_users = function ( token, callback ) {
+
+ mongoFactory.getConnection(uri).then(function (db) {
+ var users = db.collection('users');
+ users.createIndex( // make the following fields searchable
+ {
+ "user_name":"text",
+ "f_name":"text",
+ "l_name":"text"
+ });
+ users.find(
+ { $text: { $search: token } },
+ { score: { $meta: "textScore" } }
+ ).sort( { score: { $meta:"textScore" } } ).toArray(function (err, docs) {
+ if (err) callback(false, "Error: some error while searching");
+ else {
+ callback(true, docs);
+ }
+ });
+
+
+
+ }).catch(function (err) {
+ console.error(err);
+ });
+};
+
+
+/**
+ * This function will return a list of followers of user i.e. a list of
+ * exam_ids that the user has chosen to follow
+ *
+ * @param {string} user_name: the unique user name for the user
+ * @param {function} callback: with 2 args: (boolean, <string>),
+ * where boolean : err ? false : true
+ * where <string> can be error message
+ * OR on success <[<strings>]> RESULT
+ **/
+exports.retrieveFollows = function (user_name, callback) {
+
+ mongoFactory.getConnection(uri).then(function (db) {
+
+ // find the solutions table
+ var users = db.collection('users');
+ // insert data into table
+ users.find( {user_name: user_name} ).toArray(function (err, docs) {
+ // if (err) throw err;
+ if (err) callback(false, "Error: followers could not be retrieved for some reason");
+ else {
+ callback(true, docs[0].followers);
+ }
+ });
+
+ }).catch(function (err) {
+ console.error(err);
+ });
+};
+
+/**
+ * This function will retrieve ALL the comments a user has ever made.
+ * It returns an array containing objects of the form: {exam_id, comment, date, ...
+ * course_code, year, term}.
+ * Note: a comment should only exist IF a solution exists.
+ *
+ * @param {string} username: the unique user name for the user
+ * @param {function} callback: with 2 args: (boolean, <string>),
+ * where boolean : err ? false : true
+ * where <string> can be error message
+ * OR on success <[Objs]> RESULT
+ **/
+exports.retrieve_userComments_history = function (username, callback) {
+
+ // get a connection
+ mongoFactory.getConnection(uri).then(function (db) {
+ var solutions = db.collection('solutions');
+ var exams = db.collection('exams');
+ var mylist = [];
+ solutions.aggregate([
+
+ { $match : {
+ "comments.by": username
+ }},
+ { $unwind : "$comments" },
+ { $match : {
+ "comments.by": username
+ }},
+ {$project: {
+ comment: "$comments.text",
+ date: "$comments.date",
+ exam_id: "$exam_id",
+ _id: 0
+ }}
+ ]).toArray(function (err, res) {
+ if(!res.length){ // Ensure a callback is executed if res is empty
+ callback(true, res);
+ }else{
+ var finised = _.after(res.length, doCall); // execute "doCall" only after res.length # of attempts
+ res.forEach(function (comment) {
+ exams.find( { _id: ObjectId(comment.exam_id) } ).toArray(function (err, docs) {// get the exam info
+ comment.course_code = docs[0].course_code;
+ comment.year = docs[0].year;
+ comment.term = docs[0].term;
+ mylist.push(comment); // save it to array
+ finised();
+ });
+ });
+ }
+ });
+
+ function doCall() {
+ callback(true, mylist);
+ }
+
+
+ }).catch(function (err) {
+ callback(false, "Error: failed to connect to db");
+ })
+};
+
+/** We CAN use this. IF we do, we should remove the comments_count field from a user
+ * IF we dont wanna go that route, need to update these fields whenever they are altered
+ * by the user manually.
+ *
+ * @param {string} username: the unique user name for the user
+ * @param {function} callback: with 2 args: (boolean, <string>),
+ * where boolean : err ? false : true
+ * where <string> can be error message
+ * OR on success <int> RESULT
+ **/
+exports.retrieve_userComments_count = function (username, callback) {
+
+ exports.retrieve_userComments_history(username, function (bool, results) {
+ if (!bool) callback(false, "Error: error occurred");
+ else {
+ var length = results.length;
+ callback(true, length);
+ }
+ });
+
+};
+
+/**
+ * This function will retrieve ALL the solutions a user has ever provided.
+ * It returns an array containing objects of the solution form.
+ *
+ * @param {string} username: the unique user name for the user
+ * @param {function} callback: with 2 args: (boolean, <string>),
+ * where boolean : err ? false : true
+ * where <string> can be error message
+ * OR on success <[Objs]> RESULT
+ **/
+exports.retrieve_userSolutions_history = function (username, callback) {
+
+ // get a connection
+ mongoFactory.getConnection(uri).then(function (db) {
+
+ var solutions = db.collection('solutions');
+ solutions.find( { author: username } ).toArray(function (err, result) {
+ if (err) callback(false, "Error: problem while looking for stuff");
+ else {
+ callback(true, result);
+ }
+
+ });
+
+ }).catch(function (err) {
+ // console.err(err);
+ callback(false, "Error: failed to connect to db");
+ })
+};
+
+/** We CAN use this. IF we do, we should remove the solutions_count field from a user
+ * IF we dont wanna go that route, need to update these fields whenever they are altered
+ * by the user manually.
+ *
+ * @param {string} username: the unique user name for the user
+ * @param {function} callback: with 2 args: (boolean, <string>),
+ * where boolean is false if err OR true if no error
+ * where <string> can be error message
+ * OR on success <int> RESULT
+ **/
+exports.retrieve_userSolutions_count = function (username, callback) {
+
+ exports.retrieve_userSolutions_history(username, function (bool, results) {
+ if (!bool) callback(false, "Error: error occured");
+ else {
+ var length = results.length;
+ callback(true, length);
+ }
+ });
+
+};
+
+/**
+ * This function creates and adds a user to users table.
+ * IFF both the email and the user_name are not in the database already.
+ * If either of them exist, the user is NOT added.
+ *
+ * @param {string[]} fields: [email, user_name, f_name, l_name, uni, department, password, phone_num]
+ * @param {function} callbackUser: of the form (<boolean1>, <boolean2>, <string>)
+ * where, <boolean1> -
+ * <boolean2> -
+ * <string> - error ? error_mssg : success_mssg
+ **/
+exports.add_user = function (fields, callbackUser) {
+ // create a user object
+ var user_data = {
+ email: fields[0],
+ user_name: fields[1],
+ f_name: fields[2],
+ l_name: fields[3],
+ university: fields[4],
+ department: fields[5],
+ answered: 0,
+ messages: 0,
+ comments: 0,
+ phone_num: fields[7],
+ followers: []
+ };
+
+ var login_data = {
+ email: fields[0],
+ user_name: fields[1],
+ password: fields[6]
+ };
+
+ // find out if this user already exists by checking their email
+ exports.find_user( fields[0], function (result) {
+ if (result == false) {
+
+ // find out if the user_name is taken
+ exports.find_user_name( fields[1], function (docs) {
+ if (docs == false) { // if not ...
+ // continue
+
+ // when both are valid add the user to the users and logins table
+ mongoFactory.getConnection(uri)
+ .then(function (db) {
+
+ var users = db.collection('users');
+ var logins = db.collection('logins');
+
+ // Add users, and login through callbacks
+ users.insertOne( user_data, function (err) {
+ if (err) {
+ callbackUser(false, true, "Error : User has not been added.");
+
+ }
+
+ else {// user insert successfull
+ logins.insertOne(login_data, function (err) {
+ if (err) {
+ callbackUser(false, true, "Error : User has not been added.");
+
+ }
+
+ else {// login insert successfull
+ callbackUser(true, false, "User has been added.");
+
+ }
+ });
+ }
+ });
+
+ })
+ .catch(function (err) {
+ callbackUser(false, true, "Unable to connect.");
+ })
+ }
+ else {
+ callbackUser(false, false, "Username is taken.");
+ }
+ });
+ }
+ else {
+ callbackUser(false, false, "User with this email already exists.");
+ }
+ });
+};
+
+
+
+
+/**
+ * This function returns whether the user with the given username has already been signed in before.
+ * This is important because we need to link a unique facebook account with a username. If the user has signed in
+ * before, return the verification object, consisting of facebook profile id.
+ *
+ * @param {string} username
+ * @param {function} callbackUser: of the form (<boolean>, verifications object (See COLLECTION)) -
+ * callbackUser(error, object)
+ *
+ **/
+
+exports.userVerifiedBefore = function(username, callback) {
+ mongoFactory.getConnection(uri)
+ .then(function (db) {
+ var verif = db.collection('verifications');
+
+ verif.find( { username : username } ).toArray(function(err, result) {
+ if (err) {
+ callback(true, null);
+ } else {
+ callback(false, result);
+ }
+
+ });
+ });
+}
+
+
+/**
+ * THis function adds the user's facebook account verification to the database and links it to the user's local in site
+ * account.
+ *
+ * @param {object} - verifications object
+ * @param {function} callbackUser: of the form (<boolean>) - callback(error)
+ *
+ **/
+exports.addVerification = function(verification, callBack) {
+ mongoFactory.getConnection(uri)
+ .then(function (db) {
+ var verif = db.collection('verifications');
+
+ verif.insertOne(verification, function (err) {
+ if (err) {
+ callBack(true);
+ } else {
+ callBack(false);
+ }
+ })
+ });
+}
+
+/**
+ * This (helper) function returns true IFF user_name already exists in the database
+ *
+ * @param {string} user_name: the user name
+ * @param {function} callback: of the arg (bool)
+ * where <bool> : found ? true : false
+ * */
+exports.find_user_name = function (user_name, callback) {
+ // make a connection
+ mongoFactory.getConnection(uri)
+ .then(function (db) {
+
+ var logins = db.collection('logins');
+ var admins = db.collection('admins');
+ logins.find( { user_name: user_name } ).toArray(function (err, result) {
+ if (err) throw err;
+ else if (result.length == 0) { // nothing was found in users
+ // check if user_name is taken by admin
+ admins.find({username : user_name}).toArray(function(err, data) {
+ if (err) throw err;
+ else if (data.length == 0) {
+ callback(false);
+ }
+ else {
+ callback(true);
+ }
+ });
+ }
+ else {
+ callback(true);
+ }
+ });
+ })
+ .catch(function (err) {
+ console.err(err);
+ })
+};
+
+/**
+ * This function retrieves the user object given their user_name
+ *
+ * @param {string} username: the user name
+ * @param {function} callback: with args (<bool1>,<bool2>,<string1>,<string2>)
+ * where, <bool1> : success ? true : false
+ * where, <bool2> : error ? true : false
+ * where, <string1> : success ? {Obj} : null
+ * where, <string2> : success ? success_mssg : err_mssg
+ * */
+exports.retrieveUser = function (username, callback) {
+
+ mongoFactory.getConnection(uri).then(function (db) {
+ var users = db.collection('users');
+
+ users.find({user_name: username}).toArray(function (err, result) {
+ if (err) {
+ // callback(success, error, user, message)
+ callback(false, true, null, "Error : Could not retrieve user.");
+ }
+
+ else if (result.length) {
+ callback(true, false, result[0], "User retrieved");
+ }
+
+ else {
+ callback(false, false, null, "Username is undefined.");
+ }
+ });
+ });
+}
+
+/**
+ * Returns the hashed password given the username. Assume username exists.
+ * Used for both admins and users.
+ *
+ * @param {string} username: the user name
+ * @param {function} callback: with args (<bool>,<string>,<string>)
+ * where, <bool> : success ? true : false
+ * where, <string> : success ? "pwd" : null
+ * where, <string> : success ? success_mssg : err_mssg
+ * */
+exports.retrievePassword = function (username, callback) {
+ mongoFactory.getConnection(uri).then(function (db) {
+
+ var collection = db.collection('logins');
+
+ collection.find({user_name: username}).toArray(function(err, result) {
+ if (err) {
+ // callback(success, password, message)
+ callback(false, null, "Error : Could not retrieve password.");
+ }
+
+ else {
+ var pwd = result[0].password; //result is an array
+ callback(true, pwd, "Password retrieved");
+ }
+
+ });
+ });
+}
+
+/**
+ * This (helper) function returns true IFF email already exists in the database
+ *
+ * @param {string} email: unique email of the user
+ * @param {function} callback: <bool> : found ? true : false
+ * */
+exports.find_user = function (email, callback) {
+ // make a connection
+ mongoFactory.getConnection(uri)
+ .then(function (db) {
+
+ var logins = db.collection('logins');
+ logins.find( { email: email } ).toArray(function (err, result) {
+ if (err) throw err;
+ else if (result.length == 0) { // nothing was found so this user is new
+ callback(false);
+ }
+ else {
+ callback(true);
+ }
+ });
+ })
+ .catch(function (err) {
+ console.err(err);
+ })
+};
+
+
+/**
+ * This function retrieves the user object from the users collection given the object id.
+ *
+ * @param {string} id
+ * @param {function} callbackUser: of the form (<boolean>, user object (See COLLECTION)) -
+ * callbackUser(error, object)
+ *
+ **/
+exports.findUserByID = function (id, callback) {
+ // make a connection
+ mongoFactory.getConnection(uri)
+ .then(function (db) {
+ var logins = db.collection('logins');
+ logins.find( { _id : id }, function (err, result) {
+ callback(err, result);
+
+ });
+ })
+ .catch(function (err) {
+ console.err(err);
+ })
+};
+
+/************************* COURSES / EXAMS **********************************/
+
+
+/**
+ * Remvove a course from ONLY the courses table IN CASE of accidental
+ * addition.
+ *
+ * @param {string} course_code: the course code
+ * @param {function} callback: 2 args: (boolean, <string>),
+ * where <boolean> : err ? false : true
+ * where <string> : error ? error_mssg : success_mssg
+ */
+exports.remove_course = function (course_code, callback) {
+
+ mongoFactory.getConnection(uri).then(function(db) {
+
+ // fetch the exams table
+ var courses = db.collection('courses');
+
+ courses.createIndex( // make the following fields searchable
+ {
+ "course_code":"text"
+ });
+ // look for the specific course
+ courses.removeOne( { $text: { $search: course_code } }, function (err, docs) {
+ // if (err) throw err;
+ if (err) callback(false, "Error: Failed to remove the course.");
+
+ else if (docs.deletedCount == 1) {
+ callback(true, "Course was removed successfully from JUST courses");
+
+ }
+ else if (docs.deletedCount == 0) {
+ callback(false, "No such user was found");
+
+ }
+ });
+ }).catch(function(err) {
+ console.error(err);
+ });
+};
+
+
+/**
+ * This function will add the given exam_id to the given user's followers list.
+ * It simply appends the exam_id to the list and nothing else.
+ * If the exam_id already exists in the user's followers list,
+ * false will be returned, andnothing will be added.
+ *
+ * Ideally the user shouldnt even be able to attempt to follow an exam twice.
+ *
+ * @param {string} user_name: the unique user name for the user
+ * @param {string} exam_id: the _id of the exam TO follow
+ * @param {function} callback: with 2 args: (boolean, <string>),
+ * where <boolean> : err ? false : true
+ * where <string> : error ? err_messg : success_messg
+ **/
+exports.followExam = function (user_name, exam_id, callback) {
+
+ exports.retrieveFollows(user_name, function (bool, result) {
+ if (!bool) console.log(result);
+ else { // no err occured so far...
+
+ var found = false;
+ for (var i = 0; i < result.length; i++) { // search through the list of exams followed
+ if (result[i] == exam_id) {
+ found = true;
+ }
+ }
+
+ if (found) { // means exam is already followed by user
+ callback(false, "user is already following this exam");
+ }
+
+ else { // add it to the user follower list
+ mongoFactory.getConnection(uri).then(function (db) {
+
+ // find the user table
+ var users = db.collection('users');
+ // insert data into table
+ users.updateOne( {user_name: user_name}, {$push: {followers: exam_id}} , function (err) {
+ // if (err) throw err;
+ if (err) callback(false, "Error: some error occurred while following the exam");
+ else {
+ callback(true, "Success: user is following this exam");
+ }
+ });
+
+
+ }).catch(function (err) {
+ console.error(err);
+ });
+ }
+ }
+ });
+};
+
+
+
+
+
+/**
+ * This function returns an array where each element contains info for a particular question
+ * such as the question number (_id), number of solutions (count), and number of comments
+ * (comments). [ {_id,count,comments}, {} , ...]
+ *
+ * @param {string} exam_id: the exam_id of which the info is required
+ * @param {function} callback: with arg (<[Objs]>) - RESULT
+ * */
+exports.get_exam_info_by_ID = function (exam_id, callback) {
+
+ mongoFactory.getConnection(uri)
+ .then(function (db) {
+
+ var solutions = db.collection('solutions');
+
+ solutions.aggregate(
+ [
+
+ // {$unwind: "$comments"},
+ { $match: { exam_id: exam_id }},
+ {
+ $project:
+ {
+ num_comments: { $size: "$comments" },
+ _id: "$exam_id",
+ q_id: "$q_id"
+ }
+ },
+ {
+ $group : {
+ _id : "$q_id",
+ count: { $sum: 1 },
+ comments: {$sum: "$num_comments"}
+ // num_comments: { $size: "$comments" }
+
+ }
+ }
+
+
+ ]).toArray(function (err, result) {
+ callback(result);
+
+ });
+ })
+ .catch(function (err) {
+ console.err(err);
+ })
+
+};
+
+/**
+ * CALLBACK ADDED RECENTLY, BEWARE WHEN CALLING IT
+ * This function will add a comment to the solutions table
+ *
+ * @param {string} sol_id: id of the solution to which to add the comment
+ * @param {string[]} fields: <[text, by_username]>
+ * @param {function} serverCallback: with args (<bool>,<string>)
+ * where <bool> : err ? false : true
+ * where <string> : err ? err_mssg : success_mssg
+ * */
+exports.add_comment = function (sol_id, fields, serverCallback) {
+ var date = new Date();
+ var Data = {
+ text: fields[0],
+ date: date.toString().slice(0, 24),
+ by: fields[1]
+ };
+
+ mongoFactory.getConnection(uri)
+ .then(function (db) {
+
+ // find the solutions table
+ var solutions = db.collection('solutions');
+ var users = db.collection('users');
+ // insert data into table
+ solutions.updateOne( {_id: ObjectId(sol_id)}, {$push: {comments: Data}} , function (err, result) {
+ if (err) {
+ serverCallback(false, "Error: Solution found, but could not update comments.");
+ throw err;
+ }
+ else {
+ users.updateOne( { user_name: fields[1] }, { $inc: { comments: 1} }, function (err) {
+ if (err) serverCallback(false, "Error: Some error occured while updating user info");
+ else {
+ serverCallback(true, "Success: comment added successfully");
+ }
+
+ });
+ }
+
+ });
+
+ })
+ .catch(function (err) {
+ serverCallback(false, "Error: Could not establish connection with database.");
+ console.error(err);
+ })
+};
+
+/**
+ * This function will get all the solution for a given exam_id and q_num
+ * sorted by highest to lowest votes.
+ *
+ * @param {string} exam_id: exam_id for which the info is required.
+ * @param {int} q_num: the question number for which solutions are required.
+ * @param {function} callback: <[Objs]> - RESULT
+ * */
+exports.get_all_solutions = function (exam_id, q_num, callback) {
+
+ // check if the exam id already exists...
+ exports.get_exam_byID(exam_id, function (success, failure, exam) {
+ if (!success && failure) { // some error occurred while searching
+ callback(false, true, "Error: Some error occurred while searching for exam", null);
+ } else if (!success && !failure) {
+ callback(false, true, "Error: this exam does not exist", null);
+ } else if (success && !failure) { // this exam exists proceed with task
+ mongoFactory.getConnection(uri).then(function (db) {
+
+ var solutions = db.collection('solutions');
+
+ solutions.find(
+ {
+ exam_id: exam_id,
+ q_id: q_num
+ }
+ ).sort({ votes: -1}).toArray( function (err, docs) {
+ if (err) callback(false, true, "Error: Some error occurred while looking for solutions");
+ else { // either nothing was found or something was found
+ callback(true, false, "Solutions", docs);
+ }
+
+ });
+ }).catch(function () {
+ callback(false, true, "Error: Some error occurred with the db connection", null);
+ })
+ }
+ });
+
+};
+
+/**
+ * CALLBACK ADDED RECENTLY, BEWARE WHEN CALLING IT
+ * This function will add a solution to the solutions table in the database.
+ *
+ * @param {string[]} fields: [exam_id , question_id, solution text, user_name]
+ * @param {function} callback: with args (bool, string)
+ * where, <bool>: err ? false : true
+ * where, <string>: err ? err_mssg : success_mssg
+ * */
+exports.add_solution = function (fields, callback) {
+ var Data = {
+ exam_id: fields[0],
+ q_id: fields[1],
+ text: fields[2],
+ votes: 0,
+ comments: [],
+ author: fields[3]
+ };
+
+ // establish a connection
+ mongoFactory.getConnection(uri).then(function(db) {
+
+ // find the solutions table
+ var solutions = db.collection('solutions');
+ var users = db.collection('users');
+ // insert data into table
+ solutions.insert(Data, function(err) {
+ if (err) callback(false , "Error: Failed to add the solution");
+ else {
+ users.updateOne( { user_name: fields[3] }, { $inc: { answered: 1} }, function (err) {
+ if (err) callback(false, "Error: Failed to update the user solution count");
+ else {
+ callback(true, "Success: added solution successfully!");
+ }
+ });
+ }
+ });
+
+
+ }).catch(function(err) {
+ console.error(err);
+ });
+
+};
+
+/**
+ * This function will update the vote count of a solution.
+ *
+ * @param {string} sol_id: the sol_id of the solution to vote
+ * @param {string} upORdown: <string> : up_vote ? "up" : "down"
+ * @param {function} callback: with args (bool, string)
+ * where, <bool>: err ? false : true
+ * where, <string>: err ? err_mssg : success_mssg
+ * */
+exports.vote_solution = function (sol_id, upORdown , callback) {
+ var vote = (upORdown == "up") ? 1 : -1;
+
+ mongoFactory.getConnection(uri).then(function (db) {
+ var solutions = db.collection('solutions');
+ solutions.updateOne(
+ {_id: ObjectId(sol_id) },
+ { $inc: { votes: vote} }, function (err) {
+ // if (err) throw err;
+ if (err) callback(false, "Error: couldnt update the vote count");
+ else {
+ callback(true, "Success: updated vote count");
+ }
+
+ });
+ }).catch(function (err) {
+ console.log(err);
+ });
+};
+
+/**
+ * This function will retrieve all exams in the database given the course code ...
+ * ... ordered by the year of the exam.
+ *
+ * @param {string} course_code: the course code to get all the exams for
+ * @param {function} callback: with args (<string>)
+ * where on success is <[Objs]>
+ * */
+exports.get_all_exams = function (course_code, callback) {
+
+ // get a connection
+ mongoFactory.getConnection(uri)
+ .then(function(db) {
+
+ // get the exams table
+ var exam_collection = db.collection('exams');
+
+ exam_collection.createIndex( // make the following fields searchable
+ {
+ "course_code":"text"
+ });
+ // search exams table with given course code
+ exam_collection.find(
+ { $text: { $search: course_code } }
+ ).sort({ year: -1}).toArray( function (err, docs) { // order by year
+ if (err) throw err;
+
+ else { // get the title
+ exports.find_course(course_code, function (result, data) {
+
+ if (result == true) {
+ // append the title from data to each exam object from docs
+ docs.forEach(function (doc) {
+ doc.title = data[0].title;
+ });
+
+ if (debug_mode == true){
+ console.log(docs);
+ console.log(data);
+ }
+ /*callback(docs); // send back the data*/
+ }
+ else if (result == false) { // no such course was found
+ console.log("This course has not been added to the database.");
+ }
+ callback(docs); // send back the data
+
+ });
+ }
+ });
+ })
+ .catch(function(err) {
+ console.error(err);
+ });
+
+};
+
+/**
+ * This function will add an exam to the database UNLESS the exams already exists.
+ * If the exams table is empty, this will create one and then add the data.
+ * Note: this assumes that (course_code + year + term + type) together form a unique exam.
+ * i.e there can't be two exams occurring for the same course in the same year in the same term with the same type.
+ *
+ * @param {string[]} fields: an array of format ["course_code", year, "term",
+ * ["instructor1",...,"instructor n"], page_count, question_count
+ * "upload_date", "user_name"]
+ * @param {string[]} questions_array: a array by format ["q_1", "q_2", ... , "q_question_count"]
+ * @param {function} serverCallback: with args (<bool>, <string>)
+ * where <bool> : err ? false : true
+ * where <string> : err ? err_mssg : success_mssg
+ *
+ * */
+exports.add_exam = function (fields, questions_array, serverCallback) {
+
+ // construct an exam object
+ var Data =
+ {
+ course_code: fields[0],
+ year: fields[1],
+ term: fields[2],
+ type: fields[3],
+ instructors: fields[4],
+ page_count: fields[5],
+ questions_count: fields[6],
+ questions_list: [],
+ upload_date: fields[7],
+ uploaded_by: fields[8]
+ };
+
+ // create the questions objects
+ for (var i = 1; i <= Data.questions_count; i++) {
+ Data.questions_list.push(
+ {
+ q_id: i,
+ question: questions_array[i - 1]
+ }
+ );
+ }
+
+ // first see if the exam already exists
+ // pass in course_code, year, term and type...
+ exports.find_exam([fields[0], fields[1], fields[2], fields[3]], serverCallback, function(result, serverCallback) {
+
+ if (result == true) { // meaning that the exam was found
+ serverCallback(false, "This exam already exists in the database.");
+ }
+
+ else { // add Data to the database
+ // make a connection
+ mongoFactory.getConnection(uri)
+ .then(function(db) {
+
+ // find the exams table
+ var exam_collection = db.collection('exams');
+ // insert data into table
+ exam_collection.insert(Data, function(err) {
+ if (err) {
+ serverCallback(false, "Error: Could not add exam into database.");
+ throw err;
+ }
+ else {
+ serverCallback(true, "Exam Successfully added.");
+ }
+ });
+ })
+ .catch(function(err) {
+ serverCallback(false, "Error: Could not establish connection with database.");
+ console.error(err);
+ });
+ }
+ });
+};
+
+/**
+ * This function will return TRUE if the provided exam info already exists in the database
+ * OR FALSE if it does not exist in the database.
+ *
+ * @param {string[]} fields: an array of format ["course_code", year, "term", "type"]
+ * @param {function} serverCallback: ...
+ * @param {function} callback: with args (<bool>, ...)
+ * where <bool> : found ? true : false
+ * */
+exports.find_exam = function (fields, serverCallback, callback) {
+
+ var course_code = fields[0];
+ var year = fields[1];
+ var term = fields[2];
+ var type = fields[3];
+
+ // check the data to see if this exam exists...
+
+ // first make a connection
+ mongoFactory.getConnection(uri)
+ .then(function(db) {
+
+ // fetch the exams table
+ var exams = db.collection('exams');
+
+ // look for the exam
+ exams.find(
+ {
+ course_code: course_code,
+ year: year,
+ term: term,
+ type: type
+ }
+ ).toArray(function (err, docs) {
+ if (err) throw err;
+
+ if (docs.length == 0) { // if this exam doesnt exist.... add it
+ callback(false, serverCallback);
+ }
+ else { // exam was found
+ callback(true, serverCallback);
+ }
+ });
+ })
+ .catch(function(err) {
+ console.error(err);
+ });
+};
+
+/**
+ * This function will add a course to the database UNLESS the course already exists.
+ * If the course table is empty, this will create one and then add the data.
+ * Note: this assumes that (course_codes) are unique.
+ *
+ * @param {string} course_code: the course code
+ * @param {string} title: the course description
+ * @param {function} serverCallback: with args (<bool>, <string>)
+ * where <bool> : err ? false : true
+ * where <string> : err ? err_mssg : success_mssg
+ * */
+exports.add_course = function (course_code, title, serverCallback) {
+
+ var courseData = {
+ course_code: course_code,
+ title: title
+ };
+
+ exports.find_course(course_code, function (result) {
+
+ if (result == true){
+ serverCallback(false, "Course already exists");
+ }
+ else if (result == false) { // add it
+ mongoFactory.getConnection(uri)
+ .then(function(db) {
+
+ // find the exams table
+ var courses = db.collection('courses');
+ // insert data into table
+ courses.insert(courseData, function(err) {
+ if (err) throw err;
+ else {
+ serverCallback(true, "Course added successfully.");
+ }
+ });
+ })
+ .catch(function(err) {
+ console.error(err);
+ });
+ }
+ });
+};
+
+/**
+ * This function will return TRUE if the provided course info already exists in the database
+ * OR FALSE if it does not exist in the database.
+ *
+ * @param {string} course_code: the course code
+ * @param {function} callback: with args (<bool>, <string>)
+ * where <bool> : found ? true : false
+ * where <string> : found ? null : [{Obj}]
+ * */
+exports.find_course = function (course_code, callback) {
+ // check the data to see if this exam exists...
+
+ // first make a connection
+ mongoFactory.getConnection(uri)
+ .then(function(db) {
+
+ // fetch the courses table
+ var courses = db.collection('courses');
+ courses.createIndex( // make the following fields searchable
+ {
+ "course_code":"text"
+ });
+ // look for the courses
+ courses.find(
+ { $text: { $search: course_code } }
+ ).toArray(function (err, docs) {
+ if (err) throw err;
+ // if this course doesnt exist.... add it (via add_course call)
+ if (docs.length == 0) {
+ callback(false);
+ }
+ else { // course was found
+ callback(true, docs);
+ }
+ });
+ })
+ .catch(function(err) {
+ console.error(err);
+ });
+};
+
+/**
+ * This function will remove the exam from the database given the combination
+ * of (course_code+ year + term + type)
+ *
+ * @param {string[]} fields: an array of format ["course_code", year, "term", "type"]
+ * @param {function} serverCallback: with args (<bool>, <string>)
+ * where <bool> : success ? true : false
+ * where <string> : success ? success_mssg ? err_mssg
+ *
+ * */
+exports.remove_exam = function (fields, serverCallback) {
+
+ var course_code = fields[0];
+ var year = fields[1];
+ var term = fields[2];
+ var type = fields[3];
+
+ // establish connection
+ mongoFactory.getConnection(uri)
+ .then(function(db) {
+
+ // fetch the exams table
+ var exams = db.collection('exams');
+
+ // look for the specific exam
+ exams.removeOne(
+ {
+ course_code: course_code,
+ year: year,
+ term: term,
+ type: type
+ }, function (err, docs) {
+ if (err) throw err;
+ else {
+ if (docs.deletedCount == 1) {
+ serverCallback(true, "Exam was removed successfully");
+ }
+ else if (docs.deletedCount == 0) {
+ serverCallback(false, "No such exam was found");
+ }
+ }
+ }
+ );
+ })
+ .catch(function(err) {
+ console.error(err);
+ });
+};
+
+/**
+ * This function will get the specific exam object given its ID.
+ *
+ * @param {string} id: the exma_id
+ * @param {function} callback: with args (<bool>,<bool>,<string>)
+ * where, <bool> : success ? true : false
+ * where, <bool> : error ? true : false
+ * where, <string> : success ? {Obj}
+ * */
+exports.get_exam_byID = function (id, callback) {
+
+ // establish a connection
+ mongoFactory.getConnection(uri)
+ .then(function(db) {
+
+ // find the solutions table
+ var exams = db.collection('exams');
+ // query
+ exams.find( {_id: ObjectId(id)} ).toArray(function (err, docs) {
+ if (err) {
+ callback(false, true, null);
+ } else if (docs.length == 0) {
+ callback(false, false, null);
+ }
+ else {
+ callback(true, false, docs[0]);
+ }
+ });
+ })
+ .catch(function(err) {
+ callback(false, true, null);
+ });
+};
+
+/****************************** ADMINS *********************************/
+
+/**
+ * This function adds the admin given the admin data admin_data (see ADMINS in COLLECTIONS) into the admins collection.
+ * Does not add and returns a message if the username in admin_data already exists.
+ *
+ * @param {object} admin object (see ADMINS in COLLECTIONS)
+ * @param {function} callbackUser: of the form (<boolean1>, <boolean2>, <string>) -
+ * callback(error, success, message)
+ *
+ **/
+
+exports.addAdmin = function (admin_data, callback) {
+ var logins = db.collection('logins');
+ // Check if the admin username already exists. Also check for user username. If it does, then we don't add the
+ // admin_data and return a message.
+ exports.adminExists( admin_data.username , function (error, exists, data, message) {
+ if (!exists && !error) {
+ logins.find({username: username}).toArray(function (err, result) {
+ if (err){
+ callback(true, false, "Error: could not retreive logins collection in addAdmin().");
+ } else if (result.length) {
+ callback(false, false, 'User with given username already exists.');
+ } else {
+ mongoFactory.getConnection(uri).then(function (db) {
+
+ var admins = db.collection('admins');
+
+ admins.insertOne(admin_data, function (err) {
+ if (err) {
+ callback(false, true, message);
+ }
+ else {
+ callback(true, false, "Admin added.");
+ }
+
+ });
+ });
+ }
+ }).catch(function (err) {
+ callbackUser(false, true, "Unable to connect.");
+ })
+ } else {
+ callback(error, exists, message);
+ }
+ });
+
+};
+
+/**
+ * This function checks if the admin username already exists in the ADMINS collection. If it does, then callback
+ * returns success, and the admin object is passed through the callback.
+ *
+ * @param {string} username - admin username
+ * @param {function} callbackUser: of the form (<boolean1>, <boolean2>,<object>, <string>) -
+ * callback(error, success, object, message)
+ *
+ **/
+exports.adminExists = function (username, callback) {
+ // make a connection
+ mongoFactory.getConnection(uri).then(function (db) {
+
+ var admins = db.collection('admins');
+ admins.find( { username: username } ).toArray(function (err, result) {
+ if (err) {
+ callback(true, false, null, "Error: could not retrieve admin in adminExists().");
+ } else if (result.length) {
+ callback(false, true, result[0], "Admin with given username exists");
+ } else {
+ callback(false, false, null, "Admin with given username does not exist");
+ }
+ });
+ })
+ .catch(function (err) {
+ callback(true, false, "Error: could not connect to the database.");
+ })
+};
+
+/****************************** MAIL *********************************/
+
+/**
+ * This function adds the message object (see MAIL in COLLECTIONS) to the MAIL collection.
+ *
+ * @param {object} mail object
+ * @param {function} callbackUser: of the form (<boolean1>, <boolean2>, <string>) -
+ * callback(error, success, message)
+ *
+ **/
+exports.sendMail = function (mail_data, callback) {
+ exports.find_user_name(mail_data.receiver, function (exist){
+ if (exist) {
+ mongoFactory.getConnection(uri).then(function(db) {
+ var mail = db.collection('mail');
+ mail.insertOne(mail_data, function(err) {
+ if (err) {
+ callback(false, true, 'Error: could not send message.');
+ }
+ else {
+ callback(true, false, "Message sent.");
+ }
+
+ });
+ });
+ } else {
+ callback(false, false, 'The receiver\'s username is undefined.');
+ }
+ });
+}
+
+/**
+ * This function checks if the user with the given username has mail. If the user does, then sends a list of all
+ * mail through callback.
+ *
+ * @param {object} admin object (see ADMINS in COLLECTIONS)
+ * @param {function} callbackUser: of the form (<boolean1>, <boolean2>, <array of objects>, <string>) -
+ * callback(error, success, mail, message)
+ *
+ **/
+exports.checkMailbox = function (username, callback) {
+
+ mongoFactory.getConnection(uri).then(function(db) {
+
+ var mail = db.collection('mail');
+
+ mail.find({receiver: username}).toArray(function (err, data) {
+ if (err) {
+ callback(false, true, null, 'Error: could not retrieve inbox messages.');
+ } else if (!data) {
+ callback(false, false, null, 'No inbox.');
+ } else {
+ callback(true, false, data, 'Retrieved inbox');
+ }
+
+ });
+ });
+
+}
diff --git a/test/dbTests/test_main.js b/dbTests/test_main.js
index fcdf74b..fcdf74b 100644
--- a/test/dbTests/test_main.js
+++ b/dbTests/test_main.js
diff --git a/test/dbTests/testingDocs.txt b/dbTests/testingDocs.txt
index cb1290b..cb1290b 100644
--- a/test/dbTests/testingDocs.txt
+++ b/dbTests/testingDocs.txt
diff --git a/test/dbTests/users_data.json b/dbTests/users_data.json
index f10877c..f10877c 100644
--- a/test/dbTests/users_data.json
+++ b/dbTests/users_data.json
diff --git a/node_simple.js b/node_simple.js
index 1692527..c787bb0 100644
--- a/node_simple.js
+++ b/node_simple.js
@@ -422,9 +422,6 @@ exports.add_user = function (fields, callbackUser) {
});
};
-
-
-
/**
* This function returns whether the user with the given username has already been signed in before.
* This is important because we need to link a unique facebook account with a username. If the user has signed in
@@ -450,7 +447,6 @@ exports.userVerifiedBefore = function(username, callback) {
});
};
-
/**
* THis function adds the user's facebook account verification to the database and links it to the user's local in site
* account.
@@ -599,7 +595,6 @@ exports.findUserByID = function (id, callback) {
/************************* COURSES / EXAMS **********************************/
-
/**
* Remvove a course from ONLY the courses table IN CASE of accidental
* addition.
@@ -634,7 +629,6 @@ exports.remove_course = function (course_code, callback) {
});
};
-
/**
* This function will add the given exam_id to the given user's followers list.
* It simply appends the exam_id to the list and nothing else.
@@ -684,10 +678,6 @@ exports.followExam = function (user_name, exam_id, callback) {
});
};
-
-
-
-
/**
* This function returns an array where each element contains info for a particular question
* such as the question number (_id), number of solutions (count), and number of comments
diff --git a/routes/index.js b/routes/index.js
index 4117e41..b2a90dc 100644
--- a/routes/index.js
+++ b/routes/index.js
@@ -23,6 +23,16 @@ router.get('/about', function(req, res, next) {
res.render('about');
});
+/** Serve the terms_and_conditions.hbs page */
+router.get('/terms_and_conditions', function(req, res, next) {
+ res.render('terms_and_conditions');
+});
+
+/** Serve the privacy_poicy.hbs page */
+router.get('/privacy_policy', function(req, res, next) {
+ res.render('privacy_policy');
+});
+
/** Serve the profile page of a user with given username */
router.get('/public_profile/:username', function(req,res,next){
diff --git a/routes/user.js b/routes/user.js
index c40d64f..8f73f21 100644
--- a/routes/user.js
+++ b/routes/user.js
@@ -158,7 +158,6 @@ router.get('/signin', loggedOut, function (req, res, next) {
});
});
-
/** Render/GET verification page. */
router.get('/verify', function(req, res, next) {
if (req.user && req.user.email) {
@@ -169,7 +168,6 @@ router.get('/verify', function(req, res, next) {
});
-
/** Retrieves infomation from the signup form, form validates and sends it to passport.js to authenticate. */
router.post('/signup', loggedOut, function(req, res, next) {
req.assert('fname', 'Please enter a valid first name.').notEmpty().withMessage('First name required.').isAlpha();
@@ -387,7 +385,6 @@ router.post('/solution/vote/:examID/:qID/:solID', function(req, res, next){
});
-
/**
* Retrieves information about the exam following button, redirects if there is an error, and shows an error alert
* if the user is not logged in.
@@ -416,9 +413,6 @@ router.post('/follow_exam/:examID',function (req, res) {
}
});
-
-
-
module.exports = router;
/************** Route protection ********************/
diff --git a/test/dbTests/resulst.txt b/test/dbTests/resulst.txt
deleted file mode 100644
index 6d78581..0000000
--- a/test/dbTests/resulst.txt
+++ /dev/null
@@ -1,78 +0,0 @@
-*****************BEFORE***************************************************
-
-Wed Jul 27 10:42:09.669 [initandlisten] connection accepted from 127.0.0.1:44510 #20 (2 connections now open)
-Wed Jul 27 10:42:09.670 [initandlisten] connection accepted from 127.0.0.1:44511 #21 (3 connections now open)
-Wed Jul 27 10:42:09.670 [initandlisten] connection accepted from 127.0.0.1:44512 #22 (4 connections now open)
-Wed Jul 27 10:42:09.670 [initandlisten] connection accepted from 127.0.0.1:44513 #23 (5 connections now open)
-Wed Jul 27 10:42:09.670 [initandlisten] connection accepted from 127.0.0.1:44514 #24 (6 connections now open)
-Wed Jul 27 10:42:09.670 [initandlisten] connection accepted from 127.0.0.1:44515 #25 (7 connections now open)
-Wed Jul 27 10:42:09.670 [initandlisten] connection accepted from 127.0.0.1:44516 #26 (8 connections now open)
-Wed Jul 27 10:42:09.670 [initandlisten] connection accepted from 127.0.0.1:44517 #27 (9 connections now open)
-Wed Jul 27 10:42:09.670 [initandlisten] connection accepted from 127.0.0.1:44518 #28 (10 connections now open)
-Wed Jul 27 10:42:09.670 [initandlisten] connection accepted from 127.0.0.1:44519 #29 (11 connections now open)
-Wed Jul 27 10:42:09.670 [initandlisten] connection accepted from 127.0.0.1:44520 #30 (12 connections now open)
-Wed Jul 27 10:42:09.670 [initandlisten] connection accepted from 127.0.0.1:44521 #31 (13 connections now open)
-Wed Jul 27 10:42:09.670 [initandlisten] connection accepted from 127.0.0.1:44522 #32 (14 connections now open)
-Wed Jul 27 10:42:09.670 [initandlisten] connection accepted from 127.0.0.1:44523 #33 (15 connections now open)
-Wed Jul 27 10:42:09.670 [initandlisten] connection accepted from 127.0.0.1:44524 #34 (16 connections now open)
-Wed Jul 27 10:42:09.670 [initandlisten] connection accepted from 127.0.0.1:44525 #35 (17 connections now open)
-Wed Jul 27 10:42:09.670 [initandlisten] connection accepted from 127.0.0.1:44526 #36 (18 connections now open)
-Wed Jul 27 10:42:09.670 [initandlisten] connection accepted from 127.0.0.1:44527 #37 (19 connections now open)
-Wed Jul 27 10:42:09.670 [initandlisten] connection accepted from 127.0.0.1:44528 #38 (20 connections now open)
-Wed Jul 27 10:42:09.670 [initandlisten] connection accepted from 127.0.0.1:44529 #39 (21 connections now open)
-Wed Jul 27 10:42:09.670 [initandlisten] connection accepted from 127.0.0.1:44530 #40 (22 connections now open)
-Wed Jul 27 10:42:09.670 [initandlisten] connection accepted from 127.0.0.1:44531 #41 (23 connections now open)
-Wed Jul 27 10:42:09.670 [initandlisten] connection accepted from 127.0.0.1:44532 #42 (24 connections now open)
-Wed Jul 27 10:42:09.670 [initandlisten] connection accepted from 127.0.0.1:44533 #43 (25 connections now open)
-Wed Jul 27 10:42:09.670 [initandlisten] connection accepted from 127.0.0.1:44534 #44 (26 connections now open)
-Wed Jul 27 10:42:09.670 [initandlisten] connection accepted from 127.0.0.1:44535 #45 (27 connections now open)
-Wed Jul 27 10:42:09.670 [initandlisten] connection accepted from 127.0.0.1:44536 #46 (28 connections now open)
-Wed Jul 27 10:42:09.671 [initandlisten] connection accepted from 127.0.0.1:44537 #47 (29 connections now open)
-Wed Jul 27 10:42:09.671 [initandlisten] connection accepted from 127.0.0.1:44538 #48 (30 connections now open)
-Wed Jul 27 10:42:09.671 [initandlisten] connection accepted from 127.0.0.1:44539 #49 (31 connections now open)
-Wed Jul 27 10:42:09.671 [initandlisten] connection accepted from 127.0.0.1:44540 #50 (32 connections now open)
-Wed Jul 27 10:42:09.671 [initandlisten] connection accepted from 127.0.0.1:44541 #51 (33 connections now open)
-Wed Jul 27 10:42:09.671 [initandlisten] connection accepted from 127.0.0.1:44542 #52 (34 connections now open)
-Wed Jul 27 10:42:09.671 [initandlisten] connection accepted from 127.0.0.1:44543 #53 (35 connections now open)
-Wed Jul 27 10:42:09.671 [initandlisten] connection accepted from 127.0.0.1:44544 #54 (36 connections now open)
-Wed Jul 27 10:42:09.671 [initandlisten] connection accepted from 127.0.0.1:44545 #55 (37 connections now open)
-Wed Jul 27 10:42:09.671 [initandlisten] connection accepted from 127.0.0.1:44546 #56 (38 connections now open)
-Wed Jul 27 10:42:09.671 [initandlisten] connection accepted from 127.0.0.1:44547 #57 (39 connections now open)
-Wed Jul 27 10:42:09.671 [initandlisten] connection accepted from 127.0.0.1:44548 #58 (40 connections now open)
-Wed Jul 27 10:42:09.671 [initandlisten] connection accepted from 127.0.0.1:44549 #59 (41 connections now open)
-Wed Jul 27 10:42:09.671 [initandlisten] connection accepted from 127.0.0.1:44550 #60 (42 connections now open)
-Wed Jul 27 10:42:09.671 [initandlisten] connection accepted from 127.0.0.1:44551 #61 (43 connections now open)
-Wed Jul 27 10:42:09.671 [initandlisten] connection accepted from 127.0.0.1:44552 #62 (44 connections now open)
-Wed Jul 27 10:42:09.671 [initandlisten] connection accepted from 127.0.0.1:44553 #63 (45 connections now open)
-Wed Jul 27 10:42:09.671 [initandlisten] connection accepted from 127.0.0.1:44554 #64 (46 connections now open)
-Wed Jul 27 10:42:09.671 [initandlisten] connection accepted from 127.0.0.1:44555 #65 (47 connections now open)
-Wed Jul 27 10:42:09.671 [initandlisten] connection accepted from 127.0.0.1:44556 #66 (48 connections now open)
-Wed Jul 27 10:42:09.671 [initandlisten] connection accepted from 127.0.0.1:44557 #67 (49 connections now open)
-Wed Jul 27 10:42:09.671 [initandlisten] connection accepted from 127.0.0.1:44558 #68 (50 connections now open)
-Wed Jul 27 10:42:09.672 [initandlisten] connection accepted from 127.0.0.1:44559 #69 (51 connections now open)
-Wed Jul 27 10:42:09.748 [initandlisten] connection accepted from 127.0.0.1:44560 #70 (52 connections now open)
-Wed Jul 27 10:42:09.749 [initandlisten] connection accepted from 127.0.0.1:44561 #71 (53 connections now open)
-Wed Jul 27 10:42:09.751 [initandlisten] connection accepted from 127.0.0.1:44562 #72 (54 connections now open)
-Wed Jul 27 10:42:09.755 [initandlisten] connection accepted from 127.0.0.1:44563 #73 (55 connections now open)
-Wed Jul 27 10:42:09.783 [conn71] build index db.users { _id: 1 }
-Wed Jul 27 10:42:09.783 [conn71] build index done. scanned 0 total records. 0 secs
-Wed Jul 27 10:42:09.795 [conn72] build index db.logins { _id: 1 }
-Wed Jul 27 10:42:09.795 [conn72] build index done. scanned 0 total records. 0 secs
-
-*****************AFTER***************************************************
-
-Wed Jul 27 10:46:50.430 [initandlisten] connection accepted from 127.0.0.1:44615 #12 (1 connection now open)
-Wed Jul 27 10:46:50.464 [initandlisten] connection accepted from 127.0.0.1:44616 #13 (2 connections now open)
-Wed Jul 27 10:46:50.468 [initandlisten] connection accepted from 127.0.0.1:44617 #14 (3 connections now open)
-Wed Jul 27 10:46:50.471 [initandlisten] connection accepted from 127.0.0.1:44618 #15 (4 connections now open)
-Wed Jul 27 10:46:50.477 [initandlisten] connection accepted from 127.0.0.1:44619 #16 (5 connections now open)
-Wed Jul 27 10:46:50.522 [conn14] build index db.users { _id: 1 }
-Wed Jul 27 10:46:50.523 [conn14] build index done. scanned 0 total records. 0 secs
-Wed Jul 27 10:46:50.539 [conn15] build index db.logins { _id: 1 }
-Wed Jul 27 10:46:50.539 [conn15] build index done. scanned 0 total records. 0 secs
-
-
-
-
-
-
diff --git a/test/routingDb_tests.js b/test/routingDb_tests.js
index d3a607f..b700f4c 100644
--- a/test/routingDb_tests.js
+++ b/test/routingDb_tests.js
@@ -1,5 +1,6 @@
var chai = require('chai');
var chaiHttp = require('chai-http');
+var dbFile = require('../node_simple');
var expect = require('chai').expect;
var assert = chai.assert;
var url = require("url");
@@ -41,6 +42,18 @@ var users = (JSON.parse(usersPull));
chai.use(chaiHttp);
+
+
+/* Set up DB connection as it is required for the test cases below*/
+dbFile.setupDB(function (success, mssg) {
+ if(success){
+ console.log('Db Connection success');
+ } else{
+ console.log('Failed to connect');
+ }
+});
+
+
/* Test simple routes that do not require database queries */
describe('Test Basic Route:', function () {
@@ -112,9 +125,11 @@ describe('Test Basic Route:', function () {
/* Public profile page response test */
describe('User profile page (public) Route:', function(){
var server;
- before(function () {
+ before(function (done) {
server = require('./test_server');
+ done();
});
+
after(function () {
server.close();
});
@@ -125,7 +140,6 @@ describe('User profile page (public) Route:', function(){
.get('/public_profile/' + username)
.end(function(err, res){
expect(res).to.have.status(200);
-
var path = res.res.req.path;
assert.equal(path, '/public_profile/' + username); // Check url path
done();
@@ -293,11 +307,11 @@ describe('Questions Search Route:', function(){
chai.request(server)
.get('/questions/batman')
.end(function(err, res){
- expect(res).to.have.status(200);
+ expect(res).to.have.status(500);
var path = res.res.req.path;
// Redirected to homepage (with err msg)
- assert.equal(path, '/');
+ assert.equal(path, '/questions/batman');
done();
});
});
diff --git a/test/test_server.js b/test/test_server.js
index 9514124..a492f2d 100644
--- a/test/test_server.js
+++ b/test/test_server.js
@@ -10,4 +10,6 @@ server.listen(port, function(){
console.log('Listening on port 3000..');
});
+
+
module.exports = server; \ No newline at end of file
diff --git a/views/partials/footer.hbs b/views/partials/footer.hbs
index b956a1f..5da4147 100644
--- a/views/partials/footer.hbs
+++ b/views/partials/footer.hbs
@@ -4,8 +4,8 @@
<div class="navbar-collapse collapse" id="footer-body">
<ul class="nav navbar-nav">
<li><a href="/about">About Us</a></li>
- <li><a href="#">Terms &amp; Conditions</a></li>
- <li><a href="#">Privacy Policy</a></li>
+ <li><a href="/terms_and_conditions">Terms &amp; Conditions</a></li>
+ <li><a href="/privacy_policy">Privacy Policy</a></li>
</ul>
</div>
<div class="navbar-header">
diff --git a/views/privacy_policy.hbs b/views/privacy_policy.hbs
new file mode 100644
index 0000000..7f8b700
--- /dev/null
+++ b/views/privacy_policy.hbs
@@ -0,0 +1,12 @@
+<main id = 'solutions-main'>
+ <div class = 'search container' id = 'solutions-div'>
+ <h3> Privacy Policy </h3>
+ <p>We do not use your personal information other than to improve the services of our website.</p>
+ <p>We do not distribute your personal information to any outside source. </p>
+ <p>You have the ability to request any change to be made to remove your information and we will respond promptly.</p>
+ <p>We will never force you to provide use with any sort of credit card or other payment info.</p>
+ <p>We do not serve 3rd party ads.</p>
+ <p>We do not make changes to any of your personal information without notifying you first, although we do reserve the right to do so.</p>
+ </div>
+
+</main>
diff --git a/views/terms_and_conditions.hbs b/views/terms_and_conditions.hbs
new file mode 100644
index 0000000..6f2aeee
--- /dev/null
+++ b/views/terms_and_conditions.hbs
@@ -0,0 +1,63 @@
+<main id = 'solutions-main'>
+ <div class = 'search container' id = 'solutions-div'>
+ <h3>Welcome to Solutions Repo!</h3>
+ <p>These terms and conditions outline the rules and regulations for the use of Solutions Repo's Website. <br >
+
+ By accessing this website we assume you accept these terms and conditions in full. Do not continue to use Solutions Repo's website if you do not accept all of the terms and conditions stated on this page.
+ </p>
+ <p>The following terminology applies to these Terms and Conditions, Privacy Statement and Disclaimer Notice and any or all Agreements: "Client", “You” and “Your” refers to you, the person accessing this website and accepting the Company’s terms and conditions. "The Company", “Ourselves”, “We”, “Our” and "Us", refers to our Company. “Party”, “Parties”, or “Us”, refers to both the Client and ourselves, or either the Client or ourselves. All terms refer to the offer, acceptance and consideration of payment necessary to undertake the process of our assistance to the Client in the most appropriate manner, whether by formal meetings of a fixed duration, or any other means, for the express purpose of meeting the Client’s needs in respect of provision of the Company’s stated services/products, in accordance with and subject to, prevailing law of Canada. Any use of the above terminology or other words in the singular, plural, capitalisation and/or he/she or they, are taken as interchangeable and therefore as referring to same.</p>
+
+ <h3>Cookies</h3>
+ <p>We employ the use of cookies. By using <a href="http://frozen-springs-49303.herokuapp.com/" onclick="__gaTracker('send', 'event', 'outbound-article', 'http://frozen-springs-49303.herokuapp.com/', 'Solutions Repo');" title="Solutions Repo">Solutions Repo</a>'s website you consent to the use of cookies in accordance with Solutions Repo’s privacy policy.</p>
+ <p>Most of the modern day interactive web sites use cookies to enable us to retrieve user details for each visit. Cookies are used in some areas of our site to enable the functionality of this area and ease of use for those people visiting. Some of our affiliate / advertising partners may also use cookies.</p>
+
+ <h3>License</h3>
+ <p>Unless otherwise stated, Solutions Repo and/or it’s licensors own the intellectual property rights for all material on Solutions Repo All intellectual property rights are reserved. You may view and/or print pages from http://frozen-springs-49303.herokuapp.com/ for your own personal use subject to restrictions set in these terms and conditions.</p>
+ <p>You must not:</p>
+ <ul>
+ <li>Republish material from http://frozen-springs-49303.herokuapp.com/</li>
+ <li>Sell, rent or sub-license material from http://frozen-springs-49303.herokuapp.com/</li>
+ <li>Reproduce, duplicate or copy material from http://frozen-springs-49303.herokuapp.com/</li>
+ </ul>
+ <p>Redistribute content from Solutions Repo (unless content is specifically made for redistribution).</p>
+
+ <h3>User Comments</h3>
+ <ol>
+ <li>This Agreement shall begin on the date hereof.</li>
+ <li>Certain parts of this website offer the opportunity for users to post and exchange opinions, information, material and data ('Comments') in areas of the website. Solutions Repo does not screen, edit, publish or review Comments prior to their appearance on the website and Comments do not reflect the views or opinions of Solutions Repo, its agents or affiliates. Comments reflect the view and opinion of the person who posts such view or opinion. To the extent permitted by applicable laws Solutions Repo shall not be responsible or liable for the Comments or for any loss cost, liability, damages or expenses caused and or suffered as a result of any use of and/or posting of and/or appearance of the Comments on this website.</li>
+ <li>Solutions Repo reserves the right to monitor all Comments and to remove any Comments which it considers in its absolute discretion to be inappropriate, offensive or otherwise in breach of these Terms and Conditions.</li>
+ <li>You warrant and represent that:
+ <ol>
+ <li>You are entitled to post the Comments on our website and have all necessary licenses and consents to do so;</li>
+ <li>The Comments do not infringe any intellectual property right, including without limitation copyright, patent or trademark, or other proprietary right of any third party;</li>
+ <li>The Comments do not contain any defamatory, libelous, offensive, indecent or otherwise unlawful material or material which is an invasion of privacy</li>
+ <li>The Comments will not be used to solicit or promote business or custom or present commercial activities or unlawful activity.</li>
+ </ol>
+ </li>
+ </ol>
+
+
+ <h3>Content Liability</h3>
+ <p>We shall have no responsibility or liability for any content appearing on your Web site. You agree to indemnify and defend us against all claims arising out of or based upon your Website. No link(s) may appear on any page on your Web site or within any context containing content or materials that may be interpreted as libelous, obscene or criminal, or which infringes, otherwise violates, or advocates the infringement or other violation of, any third party rights.</p>
+
+ <h3>Reservation of Rights</h3>
+ <p>We reserve the right at any time and in its sole discretion to request that you remove all links or any particular link to our Web site. You agree to immediately remove all links to our Web site upon such request. We also reserve the right to amend these terms and conditions and its linking policy at any time. By continuing to link to our Web site, you agree to be bound to and abide by these linking terms and conditions.</p>
+
+ <h3>Removal of links from our website</h3>
+ <p>If you find any link on our Web site or any linked web site objectionable for any reason, you may contact us about this. We will consider requests to remove links but will have no obligation to do so or to respond directly to you.</p>
+ <p>Whilst we endeavour to ensure that the information on this website is correct, we do not warrant its completeness or accuracy; nor do we commit to ensuring that the website remains available or that the material on the website is kept up to date.</p>
+
+ <h3>Disclaimer</h3>
+ <p>To the maximum extent permitted by applicable law, we exclude all representations, warranties and conditions relating to our website and the use of this website (including, without limitation, any warranties implied by law in respect of satisfactory quality, fitness for purpose and/or the use of reasonable care and skill). Nothing in this disclaimer will:</p>
+
+ <ol>
+ <li>limit or exclude our or your liability for death or personal injury resulting from negligence;</li>
+ <li>limit or exclude our or your liability for fraud or fraudulent misrepresentation;</li>
+ <li>limit any of our or your liabilities in any way that is not permitted under applicable law; or</li>
+ <li>exclude any of our or your liabilities that may not be excluded under applicable law.</li>
+ </ol>
+ <p>The limitations and exclusions of liability set out in this Section and elsewhere in this disclaimer: (a) are subject to the preceding paragraph; and (b) govern all liabilities arising under the disclaimer or in relation to the subject matter of this disclaimer, including liabilities arising in contract, in tort (including negligence) and for breach of statutory duty.</p>
+ <p>To the extent that the website and the information and services on the website are provided free of charge, we will not be liable for any loss or damage of any nature.</p>
+
+ </div>
+</main>