From c9763f1c65a148dcac09c25ac98332064d7312ce Mon Sep 17 00:00:00 2001 From: roye2 <07eeroy@gmail.com> Date: Fri, 3 Jul 2026 10:05:02 -0400 Subject: [PATCH 1/3] switch instances of @Route(...) with #[Route(...)] --- .../software_and_system_design/router.md | 61 +++++++------------ 1 file changed, 21 insertions(+), 40 deletions(-) diff --git a/_docs/developer/software_and_system_design/router.md b/_docs/developer/software_and_system_design/router.md index 5e8ac515..06e3e627 100644 --- a/_docs/developer/software_and_system_design/router.md +++ b/_docs/developer/software_and_system_design/router.md @@ -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) {...} ``` @@ -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() {...} ``` @@ -98,27 +86,24 @@ 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 @@ -130,20 +115,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() ``` From 9d3eaeddd1e0596de64d096e4730c48e4259855a Mon Sep 17 00:00:00 2001 From: roye2 <07eeroy@gmail.com> Date: Fri, 3 Jul 2026 10:40:43 -0400 Subject: [PATCH 2/3] update router_response.md as well --- .../software_and_system_design/router.md | 1 - .../router_response.md | 72 ++++++++----------- 2 files changed, 28 insertions(+), 45 deletions(-) diff --git a/_docs/developer/software_and_system_design/router.md b/_docs/developer/software_and_system_design/router.md index 06e3e627..d4d9d534 100644 --- a/_docs/developer/software_and_system_design/router.md +++ b/_docs/developer/software_and_system_design/router.md @@ -97,7 +97,6 @@ Access control can be enforced easily via `#[AccessControl(...)]` annotation. Pl For example, the following route will only allow instructor access. ```php - #[AccessControl(role: "INSTRUCTOR")] #[Route("/courses/{_semester}/{_course}/course_materials/modify_timestamp")] public function modifyCourseMaterialsFilePermission($filename, $checked) diff --git a/_docs/developer/software_and_system_design/router_response.md b/_docs/developer/software_and_system_design/router_response.md index cb3506e2..c4b609f9 100644 --- a/_docs/developer/software_and_system_design/router_response.md +++ b/_docs/developer/software_and_system_design/router_response.md @@ -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 @@ -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 @@ -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 @@ -94,11 +88,9 @@ 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 @@ -106,7 +98,7 @@ 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 @@ -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" @@ -131,18 +121,16 @@ 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 @@ -150,11 +138,8 @@ 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 @@ -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 @@ -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!"), @@ -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. From bdbbede102c757f858b6ca41b5baf763b4e8d834 Mon Sep 17 00:00:00 2001 From: roye2 <07eeroy@gmail.com> Date: Fri, 3 Jul 2026 11:00:28 -0400 Subject: [PATCH 3/3] update controller info as well --- .../developing_the_php_site/controller.md | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/_docs/developer/developing_the_php_site/controller.md b/_docs/developer/developing_the_php_site/controller.md index 6b64f5f0..f91403ef 100644 --- a/_docs/developer/developing_the_php_site/controller.md +++ b/_docs/developer/developing_the_php_site/controller.md @@ -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) { @@ -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 @@ -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 {} ```