Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 7 additions & 10 deletions _docs/developer/developing_the_php_site/controller.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,12 @@ appropriate [View](view).
A request to view the `UserDetails` page would first hit the
`UserController`, which might look something like this:

```PHP
```php
/**
* Route to view the userDetailsPage.
* @Route("/{_semester}/{_course}/show_user_details", methods={"GET"})
* @AccessControl(role="INSTRUCTOR")
**/

* Route to view the userDetailsPage.
*/
#[AccessControl(role="INSTRUCTOR")]
#[Route("/{_semester}/{_course}/show_user_details", methods={"GET"})]
public function userDetailsPage($user_id) {
$user = $this->core->getQueries()->getUserFromId($user_id);
if ($user) {
Expand All @@ -41,7 +40,7 @@ From the website, they can now make a request which routes to the
`userDetails` page. Something like
`f20/sample/show_user_details?user_id="aphacker"`

Because we added `@AccessControl(role="INSTRUCTOR")`, only instructor
Because we added `#[AccessControl(role="INSTRUCTOR")]`, only instructor
level users can access this page.

The `$user_id` parameter to our function is populated from the
Expand Down Expand Up @@ -86,9 +85,7 @@ annotation in the docstring for the controller class, like so:
```php
use app\libraries\routers\Enabled;

/**
* @Enabled("forum")
*/
#[Enabled(feature: "forum")]
class ForumController {}
```

Expand Down
60 changes: 20 additions & 40 deletions _docs/developer/software_and_system_design/router.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,44 +30,36 @@ While most routes in Submitty are specific to one course, there are places that
For those routes, it is simple to set up a route.

```php
/**
* @Route("/home")
*/
#[Route("/home")]
public function showHomepage() {...}
```

#### Route with Course Information

A majority of links in Submitty require course information to return correct contents. Therefore, it is necessary for the router to know which `(term, course)` tuple is requested. It is needed to prepend the route with `/courses/{_term}/{_course}`. The course information will be loaded automatically before calling the function.
A majority of links in Submitty require course information to return correct contents. Therefore, it is necessary for the router to know which `(semester, course)` tuple is requested. It is needed to prepend the route with `/courses/{_semester}/{_course}`. The course information will be loaded automatically before calling the function.

```php
/**
* @Route("/courses/{_term}/{_course}/reports")
*/
#[Route("/courses/{_semester}/{_course}/reports")]
public function showReportPage() {...}
```

To visit the page defined above, simply go to `{base_url}/{current term}/sample/reports`.
To visit the page defined above, simply go to `{base_url}/{current semester}/sample/reports`.

#### Route with Parameters

Sometimes we may want to pass parameters to functions. Wrapping the parameter name with brackets will do the trick.

```php
/**
* @Route("/courses/{_term}/{_course}/student/{gradeable_id}")
*/
public function showHomeworkPage($gradeable_id){...}
#[Route("/courses/{_semester}/{_course}/gradeable/{gradeable_id}")]
public function showHomeworkPage($gradeable_id) {...}
```

Note that `{_term}` and `{_course}` are actually special cases of parameters that are automatically processed by the router.
Note that `{_semester}` and `{_course}` are actually special cases of parameters that are automatically processed by the router.

Also, note that parameters in the `GET` query are passed to the function too without the need for explicit definition as long as parameter names are the same.

```php
/**
* @Route("/authentication/login")
*/
#[Route("/authentication/login")]
public function loginForm($old = null) {...}
```

Expand All @@ -78,16 +70,12 @@ The value of `$old` will be set to `Submitty` if you go to `/authentication/logi
In some cases, you may want routes to match number-only parameters, or those not starting with certain words. This could be done by adding a `requirements` field in the route definition.

```php
/**
* @Route("/courses/{_term}/{_course}/notifications/{nid}", requirements={"nid": "[1-9]\d*"})
*/
public function openNotification($nid) {...}
#[Route("/courses/{_semester}/{_course}/notifications/{nid}", requirements: ["nid" => "[1-9]\d*"])]
public function openNotification($nid, $seen) {...}
```

```php
/**
* @Route("/courses/{_term}/{_course}", requirements={"_term": "^(?!api)[^\/]+", "_course": "[^\/]+"})
*/
#[Route('/courses/{_semester}/{_course}', requirements: ['_semester' => '^(?!api)[^\/]+', '_course' => '[^\/]+'])]
public function navigationPage() {...}
```

Expand All @@ -98,27 +86,23 @@ To prevent [cross-site request forgery](https://en.wikipedia.org/wiki/Cross-site
Note that, by default, the `methods` of routes are set to be `ALL`, which means the route accepts all kind of requests. Therefore, in some cases one route may be masked by another. If you have two routes with the same name, please specify the `methods` of both.

```php
/**
* @Route("/home/change_username", methods={"POST"})
*/
#[Route("/user_profile/change_preferred_names", methods: ["POST"])]
public function changeUserName(){...}
```

#### Route that Needs Access Control

Access control can be enforced easily via `@AccessControl` annotation. Please add `use app\libraries\routers\AccessControl` to the file before using it.
Access control can be enforced easily via `#[AccessControl(...)]` annotation. Please add `use app\libraries\routers\AccessControl` to the file before using it.

For example, the following route will only allow instructor access.

```php
/**
* @Route("/courses/{_term}/{_course}/course_materials/modify_permission")
* @AccessControl(role="INSTRUCTOR")
*/
#[AccessControl(role: "INSTRUCTOR")]
#[Route("/courses/{_semester}/{_course}/course_materials/modify_timestamp")]
public function modifyCourseMaterialsFilePermission($filename, $checked)
```

For more examples about `@AccessControl`, please read [the documentation in the code](https://github.com/Submitty/Submitty/blob/master/site/app/libraries/routers/AccessControl.php).
For more examples about `#[AccessControl(...)]`, please read [the documentation in the code](https://github.com/Submitty/Submitty/blob/master/site/app/libraries/routers/AccessControl.php).

#### Route for API

Expand All @@ -130,20 +114,16 @@ Any route that uses the `/api` goes through a slightly different authentication
For example, the following route is the API for list of courses:

```php
/**
* @Route("/home/courses/new", methods={"POST"})
* @Route("/api/courses", methods={"POST"})
*/
#[Route("/home/courses/new", methods: ["POST"])]
#[Route("/api/courses", methods: ["POST"])]
public function createCourse()
```

And an API route for a method within a course:

```php
/**
* @Route("/courses/{_term}/{_course}/users", methods={"GET"})
* @Route("/api/courses/{_term}/{_course}/users", methods={"GET"})
*/
#[Route("/courses/{_semester}/{_course}/users", methods: ["GET"])]
#[Route("/api/courses/{_semester}/{_course}/users", methods: ["GET"])]
public function getStudents()
```

Expand Down
72 changes: 28 additions & 44 deletions _docs/developer/software_and_system_design/router_response.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,8 @@ like docstrings. You can think of PHP functions as endpoints that get
mapped to different routes via the router.

```php
/**
* @Route("/courses/{_semester}/{_course}/gradeable/{gradeable_id}/team/new")
*/
public function createNewTeam($gradeable_id){
#[Route("/courses/{_semester}/{_course}/gradeable/{gradeable_id}/team/new")]
public function createNewTeam($gradeable_id) {...}
```

Above is an example route that points to a function in the
Expand Down Expand Up @@ -50,10 +48,8 @@ after the base-name matters.
Functions can have multiple parameters sent to them through a URL.

```php
/**
* @Route("/courses/{_semester}/{_course}/example_route/{var1}/{var2}/{var3}")
*/
public function example($var1, $var2, $var3){
#[Route("/courses/{_semester}/{_course}/example_route/{var1}/{var2}/{var3}")]
public function example($var1, $var2, $var3) {...}
```

#### Pattern Matching Route Variables
Expand All @@ -73,10 +69,8 @@ request body. By adding `methods={""}` after the URL in a route annotation you
can make sure the router matches certain types of requests only.

```php
/**
* @Route("/courses/{_semester}/{_course}/course_materials/upload", methods={"POST"})
*/
public function ajaxUploadCourseMaterialsFiles() {
#[Route("/courses/{_semester}/{_course}/course_materials/upload", methods: ["POST"])]
public function ajaxUploadCourseMaterialsFiles() {...}
```

The router will automatically check all POST requests for valid
Expand All @@ -94,19 +88,17 @@ A PHP function can have multiple routes point towards it. This is
typically done for optional parameters and the API.

```php
/**
* @Route("/courses/{_semester}/{_course}/gradeable/{gradeable_id}")
* @Route("/courses/{_semester}/{_course}/gradeable/{gradeable_id}/{gradeable_version}")
*/
public function showHomeworkPage($gradeable_id, $gradeable_version = null){
#[Route("/courses/{_semester}/{_course}/gradeable/{gradeable_id}")]
#[Route("/courses/{_semester}/{_course}/gradeable/{gradeable_id}/{gradeable_version}", requirements: ["gradeable_version" => "\d+"])]
public function showHomeworkPage($gradeable_id, $gradeable_version = null) {...}
```

The above example allows users to optionally include a
gradeable_version in the URL while still calling the same function.

#### Access Control

The router can also restrict certain users by pattern matching against the user's access group. This can be done by adding the "\@AccessControl" annotation and giving a specific role. The following roles can be used
The router can also restrict certain users by pattern matching against the user's access group. This can be done by adding the `#[AccessControl(...)]` annotation and giving a specific role. The following roles can be used
in this annotation:

* INSTRUCTOR
Expand All @@ -115,11 +107,9 @@ in this annotation:
* STUDENT

```php
/**
* @Route("/courses/{_semester}/{_course}/course_materials/edit", methods={"POST"})
* @AccessControl(role="INSTRUCTOR")
*/
public function ajaxEditCourseMaterialsFiles() {
#[AccessControl(role: "INSTRUCTOR")]
#[Route("/courses/{_semester}/{_course}/course_materials/edit", methods: ["POST"])]
public function ajaxEditCourseMaterialsFiles(bool $flush = true) {...}
```

The above example will allow only users with the role "instructor"
Expand All @@ -131,30 +121,25 @@ You can also perform access control by restricting users with certain
permissions. This can be done by passing in the "permission" field within the access control annotation.

```php
/**
* @Route(/example/access)
* @AccessControl(permission="path.read.rainbow_grades")
*/
public function examplePermissionAccess(){
#[AccessControl(permission="path.read.rainbow_grades")]
#[Route(/example/access)]
public function examplePermissionAccess() {...}
```

The above access will allow only users with permission to view the
rainbow grades directory. You can view the list of permissions under
[Access.php](https://github.com/Submitty/Submitty/blob/master/site/app/libraries/routers/AccessControl.php)

*Note* You can restrict users by permission and role at the same time by using both parameters : `@AccessControl(role="STUDENT", permission="gradeable.submit.everyone")`
*Note* You can restrict users by permission and role at the same time by using both parameters : `#[AccessControl(role="STUDENT", permission="gradeable.submit.everyone")]`


Certain controllers can have every function restricted to a certain
role, for example the controllers under `site/app/controllers/admin`.
You can annotate access across an entire class at once to prevent writing the same thing over every function.

```php
/**
* Class AdminGradeableController
* @AccessControl(role="INSTRUCTOR")
*/
class AdminGradeableController extends AbstractController {
#[AccessControl(role: "INSTRUCTOR")]
class AdminGradeableController extends AbstractController {...}
```

Every function within `AdminGradeableController.php` will share this
Expand All @@ -173,11 +158,9 @@ behave very similarly as well.

API routes always start with `/api/`. The following are examples of valid API routes.

```php
/**
* @Route("/api/courses/{_semester}/{_course}/users", methods={"GET"})
* @Route("/api/courses", methods={"POST"})
*/
```plaintext
#[Route("/api/courses/{_semester}/{_course}/users", methods={"GET"})]
#[Route("/api/courses", methods={"POST"})]
```

#### Different Response Types
Expand All @@ -198,12 +181,13 @@ The API is currently a work in progress. Submitty uses a RESTful API design and

## The MultiResponse Object

Here is a basic example of the multiResponse object:
Here is a basic example of the MultiResponse object:
```php
/**
* @Route("/example/method", methods={"GET"})
* @Route("/api/example/method", methods={"GET"})
*/
* @return MultiResponse
*/
#[Route("/example/method", methods={"GET"})]
#[Route("/api/example/method", methods={"GET"})]
public function foo(){
return new MultiResponse(
JsonResponse::getSuccessResponse("It worked!"),
Expand Down Expand Up @@ -313,4 +297,4 @@ https://submitty.myuniversity.edu/home/gradeables/foo/bar

### Using the MultiResponse Object

It is recommended you use the MultiResponse object for new controllers moving onwards
It is recommended you use the MultiResponse object for new controllers moving onwards.
Loading