# PA: Product and Presentation
## A9: Product
**Nexus** is a generalist web-based information system for managing social interactions between users by allowing them to publish their thoughts, interests, and creative content as well as see news about their friends and people of general interest. We aim to join people that share common passions by creating themed groups where people can express themselves in a safe and prosperous environment.
### 1. Installation
Below there's the link to the release with the final version of the source code in the group's Git repository:
> link.com # TODO ADICIONAR A TAG DE PA
The the full Docker command needed to start the image available at the group's GitLab Container Registry using the production database can be found below:
> sudo docker run -it -p 8000:80 --name=lbaw2261 -e DB_DATABASE="lbaw2261" -e DB_SCHEMA="lbaw2261" -e DB_USERNAME="lbaw2261" -e DB_PASSWORD="cGtzVMep" git.fe.up.pt:5050/lbaw/lbaw2223/lbaw2261
### 2. Usage
URL to our Nexus production product: https://lbaw2261.lbaw.fe.up.pt
#### 2.1. Administration Credentials
| Username | Email | Password |
| -------- |---------------- | -------- |
| Admin | admin@gmail.com | password |
Talvez mais uma pelo menos?
#### 2.2. User Credentials
| Type | Username | Password |
| ------------- | --------- | -------- |
| User | user@gmail.com | password |
| Banned User | banned@example.net | password |
### 3. Application Help
The Nexus user has the possibility of when he feels doubts how to use any nexus page just click on the ? to appear a mini tutorial of the page.



### 4. Input Validation
In the first photo check if the credentials are correct in this case the account does not exist or the email or password are incorrect.

In the second photo, check if the username only contains letters and numbers and dashes.

In the third photo, no more tags are allowed if the maximum number of tags per post is reached.

We have done a lot of input validation. Almost every controller that receives data has input validation. We have also made more input validations on the client.
Its also worthy to note that we have done input sanitation. In this case, texts containing tags will have their tags removed from them, preventing the application from xss stored.
Some more examples...


### 5. Check Accessibility and Usability
To evaluate our accessibility and usability correctness, we performed tests using 2 checklists. The results are included as PDF files in the group's repository. Links to those files can be found below:
> **Accessibility:** [Score Report](https://git.fe.up.pt/lbaw/lbaw2223/lbaw2261/-/blob/main/checklists/Checklist%20de%20Acessibilidade%20-%20SAPO%20UX.pdf)
> **Usability:** [Score Report](https://git.fe.up.pt/lbaw/lbaw2223/lbaw2261/-/blob/main/checklists/Checklist%20de%20Usabilidade%20-%20SAPO%20UX.pdf)
Websites where the checklists used can be found:
> Accessibility: https://ux.sapo.pt/checklists/acessibilidade/
> Usability: https://ux.sapo.pt/checklists/usabilidade/
We have done some popup forms with AJAX Request in Javascript. If there was no JavaScript it would still be possible to register, login, edit post and edit groups. However, we think that not having JavaScript would mostly disable every important feature of our app (including seeing the timeline and liking content) and a solution for that would implicate a bad user experience.
We haven't tried to make our website printable has it did't make a lot of sense for a social network. However, the result of printing is not that bad.
### 6. HTML & CSS Validation
To ensure code quality, we validated our HTML and CSS. The PDF results of the HTML and CSS code validation are present in the group's repository. The direct link to these files can be found below:
> **HTML:** [Validator Report Files](https://git.fe.up.pt/lbaw/lbaw2223/lbaw2261/-/tree/main/HTML_Validator)
> **CSS:** [Validator Report File](https://git.fe.up.pt/lbaw/lbaw2223/lbaw2261/-/blob/main/HTML_Validator/CSS%20VALIDATION.pdf)
In general, the results were positive. In the Html validation, there were 5 points intrisic to every page that we did no correct. Some of these seem to be from the libraries we imported. Two of them are duplicates and we found no need to fix them. In the css validation, there is nothing to point out. The only warnings given by the site were related to browser specific intructions and a warning informing that css variables were not being checked.
Code validation was made using the following tools:
> HTML: https://validator.w3.org/nu/
> CSS: https://jigsaw.w3.org/css-validator/
### 7. Revisions to the Project
During the development of this project, as it would be expected, many changes were made, thus changes to the previous reports were needed. For that reason, where we describe the revisions made to the project since the requirements specification stage.
List of changes made:
- **ER: Requirements Specification**
* Changed placeholders to business rules
* Recover Password US was changed to visitor
* Added US regarding most reported posts/comments for the administrator
* Added US:
* US337 - Video Call added
* US338 - Online Status
- **EBD: Database Specification**
* Seed updated
* Triggers update (there was a need to pass the post id to some of the notification triggers)
- **EAP: Architecture Specification and Prototype**
- New version of the OpenAPI (called *'a9_openapi.yaml'*) was made to reflect the final list of web resources
### 8. Web Resources Specification
In this final report, we updated our OpenAPI specification in YAML format to reflect the final product's web resources.
Link to the `a9_openapi.yaml` file in the group's repository can be found [here](https://git.fe.up.pt/lbaw/lbaw2223/lbaw2261/-/blob/main/a9_openapi.yaml)
```yaml=
openapi: 3.0.0
info:
version: "2.0"
title: "LBAW Nexus Web API"
description: "Web Resources Specification (A9) for Nexus"
servers:
- url: https://lbaw2261.lbaw.fe.up.pt
description: Production server
- url: localhost:8000
description: Localhost server
tags:
- name: "M01: Authentication and Individual Profile"
- name: "M02: Feed"
- name: "M03: Groups"
- name: "M04: Requests"
- name: "M05: Messages"
- name: "M06: Notifications"
- name: "M07: Search"
- name: "M08: User Administration and Static pages"
paths:
/login:
get:
operationId: R101
summary: "R101: Login Form"
description: "Provide login form. Access: PUB"
tags:
- "M01: Authentication and Individual Profile"
responses:
"200":
description: "Ok. Show log-in UI05"
post:
operationId: R102
summary: "R102: Login Action"
description: "Processes the login form submission. Access: PUB"
tags:
- "M01: Authentication and Individual Profile"
requestBody:
required: true
content:
application/x-www-form-urlencoded:
schema:
type: object
properties:
email: # <!--- form field name
type: string
password: # <!--- form field name
type: string
required:
- email
- password
responses:
"302":
description: "Redirect after processing the login credentials."
headers:
Location:
schema:
type: string
examples:
302Success:
description: "Successful authentication. Redirect to user profile."
value: "/home"
302Error:
description: "Failed authentication. Redirect to login form."
value: "/login"
# ==================================================================
/logout:
get:
operationId: R103
summary: "R103: Logout Action"
description: "Logout the current authenticated user. Access: USR, ADM"
tags:
- "M01: Authentication and Individual Profile"
responses:
"302":
description: "Redirect after processing logout."
headers:
Location:
schema:
type: string
examples:
302Success:
description: "Successful logout. Redirect to login form."
value: "/login"
# ==================================================================
/register:
get:
operationId: R104
summary: "R104: Register Form"
description: "Provide new user registration form. Access: PUB"
tags:
- "M01: Authentication and Individual Profile"
responses:
"200":
description: "Ok. Show sign-up UI"
post:
operationId: R105
summary: "R105: Register Action"
description: "Processes the Register form submission. Access: PUB"
tags:
- "M01: Authentication and Individual Profile"
requestBody:
required: true
content:
application/x-www-form-urlencoded:
schema:
type: object
properties:
email: # <!--- form field name
type: string
password: # <!--- form field name
type: string
username: # <!--- form field name
type: string
birthdate: # <!--- form field name
type: string
bio: # <!--- form field name
type: string
required:
- email
- password
- username
- birthdate
responses:
"302":
description: "Redirect after processing the register credentials."
headers:
Location:
schema:
type: string
examples:
302Success:
description: "Successful register. Redirect to user profile."
value: "/home"
302Error:
description: "Failed to register. Redirect to register form."
value: "/register"
# ==================================================================
/google_redirect:
get:
operationId: R106
summary: "R106: Redirect to Google Authentication Provider"
description: "Redirect to Google Authentication. Access: PUB"
tags:
- "M01: Authentication and Individual Profile"
responses:
"200":
description: "Redirect successful"
# ==================================================================
/callback:
get:
operationId: R107
summary: "R107: Google Authentication Provider Callback"
description: "Return from Google Authentication Provider. Access: PUB"
tags:
- "M01: Authentication and Individual Profile"
responses:
"200":
description: "Google Login callback was successful"
# ==================================================================
/resetAuthPassword:
get:
operationId: R108
summary: "R108: Change Authenticated User's Password Page"
description: "Change Authenticated User's Password. Access: USR"
tags:
- "M01: Authentication and Individual Profile"
responses:
"200":
description: "Show Change Password Form UI"
# ==================================================================
/forgot-password:
get:
operationId: R109
summary: "R109: Forgot Password Form Page"
description: "Forgot Password Form Page. Access: PUB"
tags:
- "M01: Authentication and Individual Profile"
responses:
"200":
description: "Show Forgot Password Form UI"
# ==================================================================
/forgot-password-sent:
get:
operationId: R110
summary: "R110: Forgot Password Sent Page"
description: "Forgot Password Sent Page. Access: PUB"
tags:
- "M01: Authentication and Individual Profile"
responses:
"200":
description: "Show Forgot Password Sent Page UI"
# ==================================================================
/reset_password_without_token:
post:
operationId: R111
summary: "R111: Send Password Recovery Email"
description: "Send Password Recovery Email. Access: PUB"
tags:
- "M01: Authentication and Individual Profile"
requestBody:
required: true
content:
multipart/form-data:
schema:
type: object
properties:
email:
type: string
required:
- email
responses:
"200":
description: "Show Email Sent Page UI"
"400":
description: "Email not asscociated to any account"
# ==================================================================
/reset_password_with_token:
post:
operationId: R112
summary: "R112: Password Update"
description: "Password Update Form Submission. Access: PUB"
tags:
- "M01: Authentication and Individual Profile"
requestBody:
required: true
content:
multipart/form-data:
schema:
type: object
properties:
password:
type: string
password_confirmation:
type: string
token:
type: string
required:
- password
- password_confirmation
- token
responses:
"200":
description: "Login into the account"
"400":
description: "Invalid token or password mismatch"
# ==================================================================
/password/reset/{token}:
get:
operationId: R113
summary: "R113: Get Set New Password Form"
description: "Get Set New Password Form. Access: PUB"
tags:
- "M01: Authentication and Individual Profile"
parameters:
- in: path
name: token
schema:
type: string
required: true
responses:
"200":
description: "Show Set New Password Form UI"
# ==================================================================
/profile/{username}:
get:
operationId: R114
summary: "R114: Profile page"
description: "Show the profile page . Access: PUB/USR"
tags:
- "M01: Authentication and Individual Profile"
parameters:
- in: path
name: username
schema:
type: string
required: true
responses:
"200":
description: "Ok. Show profile page"
"404":
description: "Profile not found"
# ==================================================================
/like_list/{username}:
get:
operationId: R115
summary: "R115: User liked content list"
description: "Show User liked content. Access: USR"
tags:
- "M01: Authentication and Individual Profile"
parameters:
- in: path
name: username
schema:
type: integer
required: true
responses:
"200":
description: "Show user liked content UI"
"403":
description: "Not authenticated or cannot access user liked content"
# ==================================================================
/comment_list/{username}:
get:
operationId: R116
summary: "R116 Show the comments list page"
description: "Show the comments list of the user. Access: USR"
tags:
- "M01: Authentication and Individual Profile"
responses:
"200":
description: "Ok. Show comments list of the user page"
"403":
description: "Forbidden access to resource"
"404":
description: "User Not found"
# ==================================================================
/edit_profile/{username}:
get:
tags:
- "M01: Authentication and Individual Profile"
operationId: "R117"
summary: "R117: Get user edit profile page"
description: "Get user edit profile page. Access: USR, ADM"
parameters:
- name: username
in: path
description: "User username"
required: true
schema:
type: string
responses:
"200":
description: "User profile"
"404":
description: "User not found"
"403":
description: "Forbidden"
# ==================================================================
/api/profile/{id}:
post:
operationId: R118
summary: "R118: Edit Profile"
description: "Edit Profile. Access: OWN"
tags:
- "M01: Authentication and Individual Profile"
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
username:
type: string
email:
type: string
visibility:
type: boolean
birthdate:
type: string
bio:
type: string
photo:
type: object # is it object?
responses:
"200":
description: "Profile edited successfully"
"403":
description: "Forbidden. User is not the owner of the profile"
# ==================================================================
/api/profile/{username}:
post:
operationId: R119
summary: "R119: Edit a Profile"
description: "Edit a Profile"
tags:
- "M01: Authentication and Individual Profile"
parameters:
- in: path
name: username
schema:
type: string
required: true
requestBody:
required: true
content:
multipart/form-data:
schema:
type: object
properties:
name:
type: string
email:
type: string
password:
type: string
visibility:
type: boolean
bio:
type: string
picture:
type: string
format: binary
responses:
"200":
description: "The profile was edit successfully"
"403":
description: "Forbidden access to resource, the profile is not yours"
delete:
operationId: "R120"
summary: "R120: Delete a Profile"
description: "Deletes a profile. Access: USR"
tags:
- "M01: Authentication and Individual Profile"
parameters:
- in: path
name: username
schema:
type: string
required: true
responses:
"200":
description: "The profile was deleted successfully"
"403":
description: "Forbidden access to resource, the profile is not yours"
# ==================================================================
/broadcast/auth:
get:
tags:
- "M01: Authentication and Individual Profile"
operationId: "R121"
summary: "RR121: Get auth in pusher"
description: "Get auth token from the server so that the client can connect to pusher presence channel and make pusher requests. Access: USR"
responses:
"200":
description: "Auth"
"403":
description: "Forbidden"
"419":
description: "Not a valid CSRF token"
# ==================================================================
/:
get:
operationId: R201
summary: "R201: Website root redirect to home"
description: "Website root. Access: PUB"
tags:
- "M02: Feed"
responses:
"302":
description: "Redirect to home"
headers:
Location:
schema:
type: string
examples:
302:
description: "Redirect to home"
value: "/home"
# ==================================================================
/home:
get:
operationId: R202
summary: "R202 View Home Page"
description: "Show the home page. Access: PUB"
tags:
- "M02: Feed"
responses:
"200":
description: "Ok. Show home page UI"
# ==================================================================
/post/{id}:
get:
operationId: R203
summary: "R203: View Post Page"
description: "Shows post, with comments, likes etc. Access: PUB/USR"
tags:
- "M02: Feed"
parameters:
- in: path
name: id
schema:
type: integer
required: true
responses:
"200":
description: "Ok, show Post Page UI02"
"403":
description: "Forbidden access to resource, the post might be private or it does not exists"
# ==================================================================
/api/post/feed/{type_feed}/offset/{offset}:
get:
operationId: R204
summary: "R204 Get Posts"
description: "Gets the posts according to the parameters and returns the results as json. Access: USR and PUB for viral"
tags:
- "M02: Feed"
parameters:
- in: path
name: type_feed
description: 'Filter parameter. Can be "for_you", "friends", "groups", "viral" '
schema:
type: string
required: true
- in: path
name: offset
description: Offset of content to show.
schema:
type: integer
required: true
responses:
"200":
description: "Success"
content:
application/json:
schema:
type: array
items:
type: object
properties:
id:
type: integer
text:
type: string
post_date:
type: string
owner:
type: string
photo:
type: string
likes_count:
type: integer
comments_count:
type: integer
ranking:
type: number
images:
type: array
items:
type: string
hasLiked:
type: boolean
isOwner:
type: boolean
auth:
type: integer
example:
- id: 123
text: "The dark side of the force is stronger!!!"
post_date: "2022-05-04 22:22:22"
owner: Darth Vader
photo: user/1.png
likes_count: 10
comments_count: 5
ranking: 4.1292965
images:
- post/666.png
- post/667.png
hasLiked: true
isOwner: false
auth: 0
# ==================================================================
/api/comment:
put:
operationId: R205
summary: "R205: Edit comment"
description: "Edit comment. Access: OWN"
tags:
- "M02: Feed"
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
id_comment:
type: integer
text:
type: string
required:
- id_comment
- text
responses:
"200":
description: "The comment was edited successfully"
"403":
description: "Forbidden access to resource"
# ==================================================================
/api/comment/{id_post}:
post:
operationId: "R206"
summary: "R206: Create Comment"
description: "Create a Comment associated with a certain Post. Access: USR"
tags:
- "M02: Feed"
parameters:
- in: path
name: id_post
schema:
type: integer
required: true
responses:
"200":
description: "Comment created successfully"
"403":
description: "Forbidden access to resource"
# ==================================================================
/api/comment/{id_comment}:
delete:
tags:
- "M02: Feed"
operationId: "R207"
summary: "R207: Delete Comment"
description: "Delete Comment. Access: OWN, ADM"
parameters:
- name: id_comment
in: path
description: "coment_id"
required: true
schema:
type: string
responses:
"200":
description: "Comment deleted"
"403":
description: "Forbidden"
"419":
description: "Not a valid CSRF token"
# ==================================================================
/api/post/{id}:
delete:
operationId: R208
summary: "R208: Delete Post"
description: "Deletes a post. Access: OWN"
tags:
- "M02: Feed"
parameters:
- in: path
name: id
schema:
type: integer
required: true
responses:
"200":
description: "The post was deleted successfully"
"403":
description: "Forbidden access to resource, the post is not yours"
post:
operationId: R209
summary: "R209: Edit a Post"
description: "Edit a post. Access: OWN"
tags:
- "M02: Feed"
requestBody:
required: true
content:
application/x-www-form-urlencoded:
schema:
type: object
properties:
text:
type: string
picture:
type: array
items:
type: string
format: binary
required:
- text
responses:
"200":
description: "The post was edit successfully"
"403":
description: "Forbidden access to resource, the post is not yours"
# ==================================================================
/api/like_post:
post:
operationId: "R210"
summary: "R210: Toggle like on post"
description: "Toggle like on post. Access: USR"
tags:
- "M02: Feed"
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
id_user:
type: integer
id_post:
type: integer
responses:
"200":
description: "The like was sucessful"
"403":
description: "Forbidden access to resource"
# ==================================================================
/api/like_comment:
post:
operationId: "R211"
summary: "R211: Toggle like on comment"
description: "Toggle like on comment. Access: USR"
tags:
- "M02: Feed"
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
id_user:
type: integer
id_comment:
type: integer
responses:
"200":
description: "The like was sucessful"
"403":
description: "Forbidden access to resource"
# ==================================================================
/api/post:
post:
operationId: R212
summary: "R212: Create a Post"
description: "Creates a post. Access: USR"
tags:
- "M02: Feed"
requestBody:
required: true
content:
multipart/form-data:
schema:
type: object
properties:
text:
type: string
group:
type: string
picture:
type: array
items:
type: string
format: binary
required:
- text
responses:
"201":
description: "The post was created successfully"
"400":
description: "There is something wrong with the request body"
# ==================================================================
/group/{name}:
get:
operationId: R301
summary: "R301: Group Profile page"
description: "Group Profile. Access: PUB/USR"
tags:
- "M03: Groups"
parameters:
- in: path
name: name
schema:
type: string
required: true
responses:
"200":
description: "Success. Show group page"
"404":
description: "Group Profile does not exist"
# ==================================================================
/group/{name}/edit:
get:
operationId: "R302"
summary: "R302 Page to edit group"
description: "Page to edit group. Access: OWN"
tags:
- "M03: Groups"
parameters:
- in: path
name: name
schema:
type: string
required: true
responses:
"200":
description: "Show Edit Group Page UI"
"403":
description: "Abort. Not allowed to edit group if not owner"
# ==================================================================
/group/member_list/{name}:
get:
tags:
- "M03: Groups"
summary: "R303: Get group members"
description: "Get group members of a group. Access: MEM, ADM (OWN of group is member)"
operationId: "R303"
parameters:
- name: name
in: path
description: "Group name"
required: true
schema:
type: string
responses:
"200":
description: "Group members"
"404":
description: "Group not found"
"403":
description: "Forbidden"
# ==================================================================
/api/group/{id_group}/owner/{id_user}:
post:
operationId: R304
summary: "R304: Promote member to owner"
description: "Promote member to owner. Access: OWN"
tags:
- "M03: Groups"
parameters:
- in: path
name: id_group
schema:
type: integer
required: true
- in: path
name: id_user
schema:
type: integer
required: true
responses:
"200":
description: "New owner added successfully"
"403":
description: "Forbidden access to resource. User is not owner of the group"
# ==================================================================
/api/group/{id_group}/member/{id_user}:
delete:
operationId: R305
summary: "R305: Remove a group a member"
description: "Deletes a group member. Access: OWN"
tags:
- "M03: Groups"
requestBody:
required: true
content:
application/x-www-form-urlencoded:
schema:
type: object
properties:
id_group:
type: integer
id_user:
type: integer
responses:
"200":
description: "The member was removed successfully"
"403":
description: "Forbidden access to resource or this member doesnt belong in this group"
# ==================================================================
/api/group:
post:
operationId: R306
summary: "R306: Create a Group"
description: "Creates a Group. Access: USR"
tags:
- "M03: Groups"
requestBody:
required: true
content:
application/json:
schema:
type: array
items:
type: object
properties:
name:
type: string
description:
type: string
visibility:
type: integer
image:
type: string
format: binary
responses:
"201":
description: "The group was created successfully"
"400":
description: "There is something wrong with the request body"
# ==================================================================
/api/group/{name}:
post:
operationId: R307
summary: "R307: Edit a Group"
description: "Edit a Group. Access: OWN"
tags:
- "M03: Groups"
parameters:
- in: path
name: name
schema:
type: string
required: true
requestBody:
required: true
content:
multipart/form-data:
schema:
type: object
properties:
name:
type: string
description:
type: string
visibility:
type: boolean
picture:
type: string
format: binary
responses:
"200":
description: "The group was edit successfully"
"403":
description: "Forbidden access to resource, the Group is not yours"
delete:
operationId: R308
summary: "R308: Delete Group"
description: "Deletes a group. Access: OWN"
tags:
- "M03: Groups"
parameters:
- in: path
name: name_group
schema:
type: string
required: true
responses:
"200":
description: "The group was deleted successfully"
"403":
description: "Forbidden access to resource, the group is not yours"
# ==================================================================
/api/group_member/{id}:
get:
operationId: R309
summary: "R309: Get group member"
description: "Get group member. Access: USR"
tags:
- "M03: Groups"
parameters:
- in: path
name: id
schema:
type: integer
required: true
responses:
"200":
description: "The group members were returned successfully"
"403":
description: "Forbidden access to resource"
delete:
operationId: R310
summary: "R310: Delete group member"
description: "Delete group member from group with id. Access: OWN"
tags:
- "M03: Groups"
parameters:
- in: path
name: id
schema:
type: integer
required: true
responses:
"200":
description: "The group member was deleted successfully"
"403":
description: "Forbidden access to resource"
# ==================================================================
/api/group/{name}/owner/{id}:
post:
operationId: R311
summary: "R311: Promote a Member"
description: "Promote a member: OWN"
tags:
- "M03: Groups"
parameters:
- in: path
name: id
schema:
type: integer
required: true
responses:
"200":
description: "The group member was promoted successfully"
"403":
description: "Forbidden access to resource"
# ==================================================================
/group_list/{username}:
get:
operationId: R312
summary: "R312: Group list of username"
description: "Group list of username. Access: USR"
tags:
- "M03: Groups"
parameters:
- in: path
name: username
schema:
type: string
required: true
responses:
"200":
description: "Success. Show group list of username page"
"404":
description: "Does not exist any user with this username"
# ==================================================================
/user/friends/requests:
get:
operationId: R401
summary: "R401 View friends requests"
description: "Show the friends requests page.Access: USR"
tags:
- "M04: Requests"
responses:
"200":
description: "Ok. Show friends requests page"
"403":
description: "Forbidden access to resource"
"404":
description: "Not found"
# ==================================================================
/group/{group_name}/requests:
get:
operationId: R402
summary: "R402: Group Requests"
description: "Group Requests. Access: OWN"
tags:
- "M04: Requests"
parameters:
- in: path
name: group_name
schema:
type: string
required: true
responses:
"200":
description: "Success. Show group requests page"
"404":
description: "Does not exist any Group Requests"
# ==================================================================
/api/user/friend/request/{id_sender}/reject:
put:
operationId: "R403"
summary: "R403 Reject a friend request"
description: "Reject a friend request. Access: USR"
tags:
- "M04: Requests"
parameters:
- in: path
name: id_sender
schema:
type: integer
required: true
responses:
"200":
description: "The request was rejected with success"
"403":
description: "You need to authenticate to use this endpoint"
# ==================================================================
/api/group/request/{id}/send:
post:
operationId: "R404"
summary: "R404: Send a group join request"
description: "Send a group join request. Access: USR"
tags:
- "M04: Requests"
parameters:
- in: path
name: id
schema:
type: integer
required: true
responses:
"200":
description: "The request was sent"
"403":
description: "You need to authenticate to use this endpoint"
# ==================================================================
/api/group/request/{id}:
delete:
operationId: R405
summary: "R405: Delete group request"
description: "Delete group request. Access: OWN"
tags:
- "M04: Requests"
parameters:
- in: path
name: id
schema:
type: integer
required: true
responses:
"200":
description: "Group request deleted successfully"
"403":
description: "Forbidden access to resource. User is not owner of the group"
# ==================================================================
/api/group/{group_name}/request/{id_sender}/accept:
put:
operationId: "R406"
summary: "R406 Accept group join request"
description: "Accept group join request. Access: OWN"
tags:
- "M04: Requests"
parameters:
- in: path
name: group_name
schema:
type: string
required: true
- in: path
name: id_sender
schema:
type: integer
required: true
responses:
"200":
description: "The request was Accepted with Success"
"403":
description: "You need to authenticate to use this endpoint"
# ==================================================================
/api/user/friend/request/{id_sender}/accept:
put:
operationId: R407
summary: "R407: Accept friend request"
description: "Accept friend request. Access: USR"
tags:
- "M04: Requests"
parameters:
- in: path
name: id_sender
schema:
type: integer
required: true
responses:
"200":
description: "Friend request accepted successfully"
"403":
description: "Forbidden. User is not the receiver of the friend request"
# ==================================================================
/api/group/{group_name}/request/{id_sender}/reject:
put:
tags:
- "M04: Requests"
summary: "R408: Reject request"
description: "Reject group request. Access: OWN"
operationId: "R408"
parameters:
- name: group_name
in: path
description: "Group name"
required: true
schema:
type: string
- name: id_sender
in: path
description: "Request sender id"
required: true
schema:
type: integer
responses:
"200":
description: "Request rejected"
"403":
description: "Forbidden"
"419":
description: "Not a valid CSRF token"
# ==================================================================
/api/user/friend/request/{id_rcv}/send:
post:
tags:
- "M04: Requests"
summary: "R409: Send friend request"
description: "Send friend request. Access: USR"
operationId: "R409"
parameters:
- name: id_rcv
in: path
description: "Request receiver id"
required: true
schema:
type: integer
responses:
"200":
description: "Friend request sent"
"403":
description: "Forbidden"
"419":
description: "Not a valid CSRF token"
# ==================================================================
/user/friends/{username}:
get:
operationId: R410
summary: "R410: View user's friend list"
description: "Show the friends of user. Access: USR"
tags:
- "M04: Requests"
responses:
"200":
description: "Ok. Show friends of user page"
"403":
description: "Forbidden access to resource"
"404":
description: "User Not found"
# ==================================================================
/messages:
get:
operationId: R501
summary: "R501: Show User's Messages Page"
description: "Show User's Messages Page. Access: USR"
tags:
- "M05: Messages"
responses:
"200":
description: "Show Message Page UI"
"403":
description: "Not authenticated or cannot access the messages"
# ==================================================================
/messages/{sender_username}:
get:
operationId: R502
summary: "R502 View message page"
description: "Show the messages page UI04. Access: USR"
tags:
- "M05: Messages"
responses:
"200":
description: "Ok. Show message page"
"403":
description: "Forbidden access to resource"
"404":
description: "Not found"
# ==================================================================
/api/message/{id}:
post:
operationId: "R503"
summary: "R503: Send a message"
description: "Send a message to a certain user. Access: USR"
tags:
- "M05: Messages"
parameters:
- in: path
name: id
schema:
type: integer
required: true
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
text:
type: string
responses:
"201":
description: "Successfully created"
"400":
description: "Text cannot be null"
"403":
description: "Failure"
# ==================================================================
/notifications:
get:
operationId: R601
summary: "R601: Notification Page"
description: "Notification Page. Access: USR"
tags:
- "M06: Notifications"
responses:
"200":
description: "Show Notification List UI"
"403":
description: "Not authenticated or cannot access the notifications"
# ==================================================================
/api/user/notifications/seen:
put:
operationId: "R602"
summary: "R602: Mark all notifications as seen"
description: "Mark all notifications as seen of a certain user. Access: USR"
tags:
- "M06: Notifications"
responses:
"200":
description: "The notifications were marked as seen"
"403":
description: "You need to authenticate to use this endpoint"
# ==================================================================
/api/user/notification/{id}/seen:
put:
tags:
- "M06: Notifications"
summary: "R603: Mark notification as seen"
description: "Mark notification as seen. Access: USR"
operationId: "R603"
parameters:
- name: id
in: path
description: "Notification id"
required: true
schema:
type: integer
responses:
"200":
description: "Notification marked as seen"
"403":
description: "Forbidden"
"419":
description: "Not a valid CSRF token"
# ==================================================================
/api/user/notifications:
get:
tags:
- "M06: Notifications"
summary: "R604: Get notifications"
description: "Get notifications. Access: USR"
operationId: "R604"
responses:
"200":
description: "Notifications"
"403":
description: "Forbidden"
"419":
description: "Not a valid CSRF token"
# ==================================================================
/api/search/{query_string}/type/{type_search}/order/{type_order}/offset/{offset}:
get:
operationId: R701
summary: "R701: Search API"
description: "Searches for works and returns the results as JSON. Access: PUB"
tags:
- "M07: Search"
parameters:
- in: path
name: query_string
description: String to use for full-text search
schema:
type: string
required: true
- in: path
name: type_search
description: "Filter parameter can be: users, groups, posts or topics."
schema:
type: string
required: true
- in: path
name: order
description: "Order parameter can be: match, date, likes, comments."
schema:
type: integer
required: true
- in: path
name: offset
description: Offset of content to show.
schema:
type: integer
required: true
responses:
"200":
description: Json Search Results
content:
application/json:
schema:
type: array
items:
type: object # It can be users, groups, posts or topics
# ==================================================================
/search/{query}:
get:
operationId: R702
summary: "R702: Search"
description: "Show Search hits page. Access: PUB"
tags:
- "M07: Search"
parameters:
- in: query
name: query
description: "Queried String"
schema:
type: string
required: true
responses:
"302":
description: "Ok. Show search page"
# ==================================================================
/about:
get:
operationId: R801
summary: "R801: About Page"
description: "Show Nexus About page. Access: PUB"
tags:
- "M08: User Administration and Static pages"
responses:
"302":
description: "Ok. Show about page UI"
"404":
description: "Page not available"
# ==================================================================
/contacts:
get:
operationId: R802
summary: "R802: Contacts Page"
description: "Show Nexus Contact page. Access: PUB"
tags:
- "M08: User Administration and Static pages"
responses:
"302":
description: "Ok. Show contacts page UI"
"404":
description: "Page not available"
# ==================================================================
/features:
get:
operationId: R803
summary: "R803: Main Features Page"
description: "Show Nexus Feature page. Access: PUB"
tags:
- "M08: User Administration and Static pages"
responses:
"302":
description: "Ok. Show features page UI"
"404":
description: "Page not available"
# ==================================================================
/admin:
get:
operationId: R804
summary: "R804: Admin Page"
description: "Show administrator page. Access: ADM"
tags:
- "M08: User Administration and Static pages"
responses:
"302":
description: "Ok. Show admin UI"
"404":
description: "Page not available"
# ==================================================================
/admin/statistics:
get:
operationId: R805
summary: "R805: Admin statistics"
description: "Show Admin statistics. Access: ADM"
tags:
- "M08: User Administration and Static pages"
responses:
"200":
description: "Show admin statistics UI"
"403":
description: "Not authenticated or cannot access admin statistics"
# ==================================================================
/admin/report/{username}:
get:
operationId: R806
summary: "R806: Get User Report"
description: "Get User Report. Access: ADM"
tags:
- "M08: User Administration and Static pages"
parameters:
- in: path
name: username
schema:
type: string
required: true
responses:
"200":
description: "Show User Report UI"
"403":
description: "Not authenticated or cannot access user report"
# ==================================================================
/api/report/reject_all/{userID}:
put:
operationId: "R807"
summary: "R807: Reject all pending reports from a user"
description: "Reject all pending reports from a user. Access: ADM"
tags:
- "M08: User Administration and Static pages"
parameters:
- in: path
name: userID
schema:
type: integer
required: true
responses:
"200":
description: "Success"
# ==================================================================
/api/report:
put:
tags:
- "M08: User Administration and Static pages"
summary: "R808: Report user"
description: "Report user. Access: ADM"
operationId: "R808"
requestBody:
description: "Report"
required: true
content:
application/json:
schema:
type: object
properties:
decision:
type: integer
id:
type: integer
responses:
"200":
description: "Report sent"
"403":
description: "Forbidden"
"419":
description: "Not a valid CSRF token"
post:
operationId: "R809"
summary: "R809: Create a report"
description: "Create a report of a post or comment. Access: USR"
tags:
- "M08: User Administration and Static pages"
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
id_post:
type: integer
id_comment:
type: integer
description:
type: string
responses:
"200":
description: "Success"
content:
application/json:
schema:
type: object
properties:
id_reporter:
type: integer
id_admin:
type: integer
report_date:
type: string
description:
type: string
decision:
type: string
id_post:
type: integer
id_comment:
type: integer
example:
- id_reporter: 1
id_admin: null
report_date: "2022-05-04 22:22:22"
description: "It's offensive"
decision: "Pendent"
id_post: 5
id_comment: null
# ==================================================================
/api/user/ban/{userID}/{time_option}:
put:
tags:
- "M08: User Administration and Static pages"
summary: "R810: Ban user"
description: "Ban user. Access: ADM"
operationId: "R810"
parameters:
- name: userID
in: path
description: "User ID"
required: true
schema:
type: integer
- name: time_option
in: path
description: "Time option"
required: true
schema:
type: integer
oneOf:
- minimum: 0
maximum: 8
responses:
"200":
description: "User banned"
"400":
description: "Invalid time option"
"403":
description: "Forbidden"
"419":
description: "Not a valid CSRF token"
# ==================================================================
/api/admin/pendent_reports/{query_string}:
get:
operationId: "R811"
summary: "R811: Get the pendent reports"
description: "Get the pendent reports. Access: ADM"
tags:
- "M08: User Administration and Static pages"
parameters:
- in: path
name: query_string
schema:
type: string
required: true
responses:
"200":
description: "Success"
content:
application/json:
schema:
type: array
items:
type: object
properties:
id:
type: integer
username:
type: string
photo:
type: string
ban_date:
type: string
decision_date:
type: string
"403":
description: "Forbidden"
# ==================================================================
/api/admin/past_reports/{query_string}:
get:
operationId: "R812"
summary: "R812: Get the past reports"
description: "Get the past reports. Access: ADM"
tags:
- "M08: User Administration and Static pages"
parameters:
- in: path
name: query_string
schema:
type: string
required: true
responses:
"200":
description: "Success"
content:
application/json:
schema:
type: array
items:
type: object
properties:
id:
type: integer
username:
type: string
photo:
type: string
ban_date:
type: string
decision_date:
type: string
"403":
description: "Forbidden"
# ==================================================================
/api/user/friend/{id}:
get:
operationId: R813
summary: "R813: User friends"
description: "Saw the user:USR"
tags:
- "M08: User Administration and Static pages"
parameters:
- in: path
name: id
description: "User"
schema:
type: string
required: true
responses:
"200":
description: "Success show the user friends page"
"403":
description: "Forbidden access to resource"
"404":
description: "User not found"
# ==================================================================
```
**Note:** Administrator can make all CRUD operations in any user profile content.
### 9. Implementation Details
#### 9.1. Libraries Used
In this Nexus Project we used many libraries, frameworks and services:
| Library | Description | Link to Example |
|:--------------------------------------------------- |:---------------------------------------------------------------------------------------------------------- |:---------------: |
| [Bootstrap](https://getbootstrap.com/) | Bootstrap was used to built the frontend of the entire Nexus Website. | [Link](https://git.fe.up.pt/lbaw/lbaw2223/lbaw2261/-/blob/main/resources/views/layouts/app.blade.php#L16) |
| [Font Awesome](https://fontawesome.com/) | Font Awesome was used for good and consistent icons across the entire Nexus Website | [Link](https://git.fe.up.pt/lbaw/lbaw2223/lbaw2261/-/blob/main/resources/views/partials/sidebar/left_sidebar.blade.php#L10) |
| [Carbon](https://carbon.nesbot.com/) | Carbon was used for date/time formatting in posts, comments and messages | [Link](https://git.fe.up.pt/lbaw/lbaw2223/lbaw2261/-/blob/main/resources/views/partials/post_item.blade.php#L18) |
| [Socialite](https://laravel.com/docs/9.x/socialite) | Socialite was used for Google Auth Sign in | [Link](https://git.fe.up.pt/lbaw/lbaw2223/lbaw2261/-/blob/main/app/Http/Controllers/Auth/LoginController.php#L57) |
| [Pusher](https://pusher.com/) | Pusher was used to create realtime notifications and serve as a presence channel for siggnaling videocalls | [Link](https://git.fe.up.pt/lbaw/lbaw2223/lbaw2261/-/blob/main/public/js/app.js#L4) |
| [Mailtrap](https://mailtrap.io/) | Mailtrap alows for password recovery mail sending/receiving | [Link](https://git.fe.up.pt/lbaw/lbaw2223/lbaw2261/-/blob/main/app/Http/Controllers/PasswordRecoverController.php#L57) |
| [IntroJS](https://introjs.com/) | To create step-by-step guided tutorials to serve as a contextual help for our users | [Link](https://git.fe.up.pt/lbaw/lbaw2223/lbaw2261/-/blob/main/public/js/app.js#L333) |
| [WebRTC API](https://webrtc.org/) | For creating Peer-to-peer comunications to share audio and video | [Link](https://git.fe.up.pt/lbaw/lbaw2223/lbaw2261/-/blob/main/public/js/app.js#L120) |
#### 9.2 User Stories
> This subsection should include all high and medium priority user stories, sorted by order of implementation. Implementation should be sequential according to the order identified below.
>
> If there are new user stories, also include them in this table.
> The owner of the user story should have the name in **bold**.
> This table should be updated when a user story is completed and another one started.
A version of the board that was used during the development of the project can be found here:
> https://git.fe.up.pt/lbaw/lbaw2223/lbaw2261/-/boards
| **Identifier** | **Name** | **Module** | **Priority** | **Team Members** | **State** |
|:--------------:|:------------------------------- |:------------------------------------------ |:------------:|:-------------------:|:---------:|
| US101 | View Public Timeline | M02: Feed | High | **João** | 100% |
| US102 | View Public Profiles | M01: Authentication and Individual Profile | High | **David** | 100% |
| US103 | Search for Public Profiles | M07: Search | High | **João** | 100% |
| US104 | Exact Match Search | M07: Search | High | **João** | 100% |
| US105 | Full-text Search | M07: Search | High | **João** | 100% |
| US106 | About US | M08: User Administration and Static pages | High | **David** | 100% |
| US107 | Main Features | M08: User Administration and Static pages | High | **David** | 100% |
| US108 | Contacts | M08: User Administration and Static pages | High | **David** | 100% |
| US109 | Search over Multiple Attributes | M07: Search | Medium | **João** | 100% |
| US110 | Search Filters | M07: Search | Medium | **João** | 100% |
| US111 | Search for Content | M07: Search | Medium | **João** | 100% |
| US112 | Search by Topic | M07: Search | Medium | **João** | 100% |
| US113 | Order by Date | M07: Search | Medium | **João** | 100% |
| US201 | Login | M01: Authentication and Individual Profile | High | **Ricardo** | 100% |
| US202 | Register | M01: Authentication and Individual Profile | High | **Ricardo** | 100% |
| US203 | Recover Password | M01: Authentication and Individual Profile | Medium | **Marco** | 100% |
| US301 | Logout | M01: Authentication and Individual Profile | High | **Ricardo** | 100% |
| US302 | View my profile | M01: Authentication and Individual Profile | High | **David** | 100% |
| US303 | Edit Profile | M01: Authentication and Individual Profile | High | **Marco**, Ricardo | 100% |
| US304 | View Personalized Timeline | M02: Feed | High | **João** | 100% |
| US305 | View Friends Profiles/Feed | M02: Feed | High | **Marco** | 100% |
| US306 | Make a Post | M02: Feed | High | **Ricardo** | 100% |
| US307 | Edit Post | M02: Feed | High | **Marco**, Ricardo | 100% |
| US308 | Delete Post | M02: Feed | High | **Ricardo** | 100% |
| US309 | View Personal notifications | M06: Notifications | Medium | **Ricardo** | 100% |
| US310 | Manage friend Requests | M04: Requests | Medium | **Ricardo** | 100% |
| US311 | Manage Friends | M04: Requests | Medium | **Ricardo**, Marco | 100% |
| US312 | Make Comments | M02: Feed | Medium | **Marco** | 100% |
| US313 | React to Post | M02: Feed | Medium | **Marco** | 100% |
| US314 | React to Comment | M02: Feed | Medium | **Marco** | 100% |
| US315 | Create groups | M03: Groups | Medium | **Marco**, Ricardo | 100% |
| US316 | Request to Join Group | M03: Groups | Medium | **Ricardo** | 100% |
| US317 | Delete account | M01: Authentication and Individual Profile | Medium | **Ricardo** | 100% |
| US318 | See Reactions | M02: Feed | Medium | **Marco** | 100% |
| US319 | Make Reports | M08: User Administration and Static pages | Medium | **Marco** | 100% |
| US320 | Likes Notification | M06: Notifications | Medium | **Ricardo** | 100% |
| US321 | Comments Notification | M06: Notifications | Medium | **Ricardo** | 100% |
| US322 | Friend Requests Notification | M06: Notifications | Medium | **Ricardo** | 100% |
| US323 | Edit Comment | M02: Feed | Medium | **Marco** | 100% |
| US324 | Delete Comment | M02: Feed | Medium | **Marco** | 100% |
| US325 | Reply to Comments | M02: Feed | Medium | **Marco** | 100% |
| US326 | Timeline Filter | M02: Feed | Medium | **João** | 100% |
| US327 | Post Topics | M02: Feed | Medium | **Marco** | 100% |
| US328 | Personalized Recommendations | M02: Feed | Medium | **Ricardo** | 100% |
| US329 | Direct Messages | M05: Messages | Medium | **Ricardo** | 100% |
| US401 | Administer User Accounts | M08: User Administration and Static pages | High | **Marco**, Ricardo | 100% |
| US402 | Administrator Accounts | M08: User Administration and Static pages | Medium | **David** | 100% |
| US403 | Remove User Posts | M08: User Administration and Static pages | Medium | **Marco** | 100% |
| US404 | (un)Ban User Accounts | M08: User Administration and Static pages | Medium | **Marco** | 100% |
| US405 | Receive Reports | M08: User Administration and Static pages | Medium | **Marco**, João | 100% |
| US501 | Edit Group Information | M03: Groups | High | **Marco**, Ricardo | 100% |
| US502 | Remove Member | M03: Groups | Medium | **Marco** | 100% |
| US503 | Manage Join Requests | M03: Groups | Medium | **Ricardo** | 100% |
| US504 | Transfer Ownership | M03: Groups | Medium | **David** | 100% |
| US505 | Remove Post From Group | M03: Groups | Medium | **Marco** | 100% |
| US601 | View Group’s Members | M03: Groups | High | **Marco** | 100% |
| US602 | Post on Group | M03: Groups | High | **Marco**, Ricardo | 100% |
| US603 | Leave Group | M03: Groups | High | **Marco** | 100% |
*Table 1: Nexus User Stories (High and Medium priority)*
| **Identifier** | **Name** | **Module** | **Priority** | **Team Members** | **State** |
|:--------------:|:---------------------------- |:------------------------------------------ |:------------:|:----------------:|:---------:|
| US114 | Ordering of Results | M07: Search | Low | **João** | 100% |
| US337 | Video Call | M05: Messages | Low | **Ricardo** | 100% |
| US406 | Posts/Comments Most Reported | M08: User Administration and Static pages | Low | **Marco**, João | 100% |
| US506 | Change Group Visibility | M03: Groups | Low | **Marco** | 100% |
| US701 | Register using API | M01: Authentication and Individual Profile | Low | **Marco** | 100% |
| US702 | Login using API | M01: Authentication and Individual Profile | Low | **Marco** | 100% |
| US338 | Online Status | M05: Messages | Low | **Ricardo** | 100% |
| US330 | Account Verification | M01: Authentication and Individual Profile | Low | N/A | 0% |
| US331 | Birthday Notifications | M06-Notifications | Low | N/A | 0% |
| US332 | Delete Messages | M05: Messages | Low | N/A | 0% |
| US333 | Polls | M02: Feed | Low | N/A | 0% |
| US334 | Manage Post Visibility | M02: Feed | Low | N/A | 0% |
| US335 | Tag friends | M02: Feed | Low | N/A | 0% |
| US336 | Block User | M08: User Administration and Static pages | Low | N/A | 0% |
| US507 | Group Announcements | M03: Groups | Low | N/A | 0% |
*Table 2: Nexus User Stories (Low priority)*
---
## A10: Presentation
### 1. Product presentation
**Nexus** is a revolutionary new social network that connects people from all walks of life. With a sleek and intuitive interface, users can easily create profiles, share their interests and passions by making posts, comments and likes and by creating or joining pre-existing thematic groups to connect with friends and family. This communication is further facilitated by Nexus built-in chat with possibility to make video calls so you can be closer to the ones you love.
URL to the final product:
> https://lbaw2261.lbaw.fe.up.pt
### 2. Video presentation
To showcase our product's main features, we created a short 2 minute MP4 video that was uploaded to Moodle.
The presentation video can also be found [here](TODO.COM)
## Revision history
Changes made during the first submission:
1. Added A9
2. Added A10
5. PA Revision
GROUP2261, 01/01/2023
- David Ferreira, up202006302@g.uporto.pt
- João Alves, up202007614@g.uporto.pt
- Marco André, up202004891@g.uporto.pt (Editor)
- Ricardo Matos, up202007962@g.uporto.pt