feat: Implement user management functionality with impersonation support
This commit is contained in:
35
.vscode/ftp-kr.sync.cache.json
vendored
35
.vscode/ftp-kr.sync.cache.json
vendored
@@ -13,7 +13,36 @@
|
||||
"lmtime": 0,
|
||||
"modified": true
|
||||
},
|
||||
"autoload": {},
|
||||
"autoload": {
|
||||
"Domain": {
|
||||
"Tasks": {
|
||||
"class.WorkTimeRepository.php": {
|
||||
"type": "-",
|
||||
"size": 3269,
|
||||
"lmtime": 0,
|
||||
"modified": false
|
||||
},
|
||||
"MailToTaskImporter.php": {
|
||||
"type": "-",
|
||||
"size": 24511,
|
||||
"lmtime": 1770587036920,
|
||||
"modified": false
|
||||
},
|
||||
"TaskAttachmentRepository.php": {
|
||||
"type": "-",
|
||||
"size": 8411,
|
||||
"lmtime": 0,
|
||||
"modified": false
|
||||
},
|
||||
"WorkTimeRepository.php": {
|
||||
"type": "-",
|
||||
"size": 3269,
|
||||
"lmtime": 0,
|
||||
"modified": false
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"ceidg.php": {
|
||||
"type": "-",
|
||||
"size": 3950,
|
||||
@@ -30,8 +59,8 @@
|
||||
},
|
||||
"config.php": {
|
||||
"type": "-",
|
||||
"size": 729,
|
||||
"lmtime": 1770583610342,
|
||||
"size": 1249,
|
||||
"lmtime": 1770587027872,
|
||||
"modified": false
|
||||
},
|
||||
"cron.php": {
|
||||
|
||||
462
CODE_INDEX.md
Normal file
462
CODE_INDEX.md
Normal file
@@ -0,0 +1,462 @@
|
||||
# CODE INDEX
|
||||
|
||||
Generated: 2026-02-09 17:02:20
|
||||
|
||||
Scope: root `*.php`, `autoload/**/*.php`, `tests/**/*.php`.
|
||||
Excluded: `libraries/**`, `templates/**`.
|
||||
|
||||
## Summary
|
||||
- Files with declarations: 41
|
||||
- Classes/interfaces/traits: 33
|
||||
- Functions/methods: 333
|
||||
|
||||
## Declarations By File
|
||||
|
||||
### `ajax.php`
|
||||
- Function: `__autoload_my_classes()` (line 3)
|
||||
|
||||
### `api.php`
|
||||
- Function: `__autoload_my_classes()` (line 4)
|
||||
|
||||
### `autoload/class.Cache.php`
|
||||
- Class: `Cache` (line 2)
|
||||
- Function: `store()` (line 4)
|
||||
- Function: `get_file_name()` (line 9)
|
||||
- Function: `fetch()` (line 20)
|
||||
|
||||
### `autoload/class.Chunk.php`
|
||||
- Class: `Chunk` (line 2)
|
||||
- Function: `__construct()` (line 62)
|
||||
- Function: `__destruct()` (line 108)
|
||||
- Function: `read()` (line 122)
|
||||
|
||||
### `autoload/class.Cron.php`
|
||||
- Class: `Cron` (line 3)
|
||||
- Function: `recursive_tasks()` (line 5)
|
||||
- Function: `import_tasks_from_email()` (line 121)
|
||||
- Function: `tasks_emails()` (line 132)
|
||||
|
||||
### `autoload/class.DbModel.php`
|
||||
- Class: `DbModel` (line 2)
|
||||
- Function: `__construct()` (line 8)
|
||||
- Function: `__get()` (line 20)
|
||||
- Function: `__set()` (line 26)
|
||||
- Function: `save()` (line 31)
|
||||
- Function: `delete()` (line 52)
|
||||
|
||||
### `autoload/class.Excel.php`
|
||||
- Class: `Excel` (line 10)
|
||||
- Function: `filename()` (line 21)
|
||||
- Function: `__construct()` (line 33)
|
||||
- Function: `headers()` (line 45)
|
||||
- Function: `send_to_file()` (line 53)
|
||||
- Function: `send()` (line 59)
|
||||
- Function: `bofMarker()` (line 70)
|
||||
- Function: `eofMarker()` (line 79)
|
||||
- Function: `left()` (line 88)
|
||||
- Function: `right()` (line 101)
|
||||
- Function: `up()` (line 111)
|
||||
- Function: `down()` (line 124)
|
||||
- Function: `top()` (line 133)
|
||||
- Function: `home()` (line 141)
|
||||
- Function: `number()` (line 151)
|
||||
- Function: `label()` (line 162)
|
||||
|
||||
### `autoload/class.Html.php`
|
||||
- Class: `Html` (line 3)
|
||||
- Function: `form_text()` (line 5)
|
||||
- Function: `input_switch()` (line 12)
|
||||
- Function: `select()` (line 19)
|
||||
- Function: `textarea()` (line 26)
|
||||
- Function: `input_icon()` (line 39)
|
||||
- Function: `input()` (line 52)
|
||||
- Function: `button()` (line 65)
|
||||
- Function: `panel()` (line 78)
|
||||
|
||||
### `autoload/class.S.php`
|
||||
- Class: `S` (line 2)
|
||||
- Function: `array_unique_multi()` (line 4)
|
||||
- Function: `number_display()` (line 16)
|
||||
- Function: `prepar_request()` (line 21)
|
||||
- Function: `seo()` (line 33)
|
||||
- Function: `no_pl_excel()` (line 48)
|
||||
- Function: `noPL()` (line 63)
|
||||
- Function: `alert()` (line 100)
|
||||
- Function: `hash()` (line 105)
|
||||
- Function: `sort_array_of_array()` (line 121)
|
||||
- Function: `json_to_array()` (line 130)
|
||||
- Function: `get_session()` (line 149)
|
||||
- Function: `del_session()` (line 154)
|
||||
- Function: `set_session()` (line 158)
|
||||
- Function: `get()` (line 163)
|
||||
- Function: `pre()` (line 184)
|
||||
- Function: `email_check()` (line 200)
|
||||
- Function: `send_email()` (line 205)
|
||||
|
||||
### `autoload/class.Tpl.php`
|
||||
- Class: `Tpl` (line 2)
|
||||
- Function: `__construct()` (line 7)
|
||||
- Function: `view()` (line 13)
|
||||
- Function: `secureHTML()` (line 21)
|
||||
- Function: `render()` (line 31)
|
||||
- Function: `__set()` (line 64)
|
||||
- Function: `__get()` (line 69)
|
||||
|
||||
### `autoload/Controllers/TasksController.php`
|
||||
- Class: `TasksController` (line 4)
|
||||
- Function: `workTime()` (line 8)
|
||||
- Function: `workTimeViewModel()` (line 20)
|
||||
- Function: `resolveTaskStatusForForm()` (line 28)
|
||||
- Function: `resolveTaskStatusForSave()` (line 38)
|
||||
- Function: `taskChangeStatus()` (line 48)
|
||||
- Function: `shouldStopTimerOnStatus()` (line 74)
|
||||
- Function: `shouldSendStatusChangeEmail()` (line 79)
|
||||
- Function: `sendEmailTaskChangeStatus()` (line 84)
|
||||
|
||||
### `autoload/controls/class.BackendSites.php`
|
||||
- Class: `BackendSites` (line 3)
|
||||
- Function: `topic_delete()` (line 5)
|
||||
- Function: `topic_accept()` (line 21)
|
||||
- Function: `topic_unaccept()` (line 31)
|
||||
- Function: `topic_save()` (line 41)
|
||||
- Function: `topic_edit()` (line 58)
|
||||
- Function: `topics()` (line 65)
|
||||
- Function: `collective_topics()` (line 70)
|
||||
- Function: `collective_topic_edit()` (line 75)
|
||||
- Function: `collective_topic_save()` (line 82)
|
||||
|
||||
### `autoload/controls/class.Crm.php`
|
||||
- Class: `Crm` (line 14)
|
||||
- Function: `client_delete()` (line 17)
|
||||
- Function: `client_save()` (line 32)
|
||||
- Function: `client_edit()` (line 51)
|
||||
- Function: `main_view()` (line 63)
|
||||
|
||||
### `autoload/controls/class.Cron.php`
|
||||
- Class: `Cron` (line 3)
|
||||
- Function: `main_view()` (line 5)
|
||||
|
||||
### `autoload/controls/class.Finances.php`
|
||||
- Class: `Finances` (line 3)
|
||||
- Function: `category_delete()` (line 5)
|
||||
- Function: `operation_save()` (line 18)
|
||||
- Function: `operation_delete()` (line 39)
|
||||
- Function: `operation_edit()` (line 54)
|
||||
- Function: `category_save()` (line 71)
|
||||
- Function: `category_edit()` (line 90)
|
||||
- Function: `operations_list()` (line 103)
|
||||
- Function: `main_view()` (line 137)
|
||||
|
||||
### `autoload/controls/class.Projects.php`
|
||||
- Class: `Projects` (line 3)
|
||||
- Function: `project_save()` (line 5)
|
||||
- Function: `project_edit()` (line 25)
|
||||
- Function: `main_view()` (line 42)
|
||||
- Function: `task_order_save()` (line 51)
|
||||
- Function: `action_mark_as_done()` (line 63)
|
||||
- Function: `action_edit()` (line 77)
|
||||
- Function: `task_update()` (line 91)
|
||||
- Function: `task_text_update()` (line 98)
|
||||
- Function: `task_text_new()` (line 104)
|
||||
- Function: `project_delete()` (line 110)
|
||||
- Function: `task_change_status()` (line 126)
|
||||
- Function: `ajax_user_tasks()` (line 137)
|
||||
- Function: `task_delete()` (line 177)
|
||||
- Function: `open_task_details()` (line 193)
|
||||
- Function: `task_details()` (line 201)
|
||||
- Function: `project_default()` (line 226)
|
||||
- Function: `tasks()` (line 235)
|
||||
|
||||
### `autoload/controls/class.Site.php`
|
||||
- Class: `Site` (line 3)
|
||||
- Function: `route()` (line 5)
|
||||
|
||||
### `autoload/controls/class.Tasks.php`
|
||||
- Class: `Tasks` (line 3)
|
||||
- Function: `task_change_dates()` (line 5)
|
||||
- Function: `task_delete()` (line 17)
|
||||
- Function: `main_view_by_ajax()` (line 34)
|
||||
- Function: `main_view()` (line 96)
|
||||
- Function: `action_change_status()` (line 183)
|
||||
- Function: `comment_delete()` (line 201)
|
||||
- Function: `comment_save()` (line 218)
|
||||
- Function: `action_delete()` (line 240)
|
||||
- Function: `action_save()` (line 259)
|
||||
- Function: `tasks_order_save()` (line 282)
|
||||
- Function: `send_email_task_change_status()` (line 292)
|
||||
- Function: `task_change_project()` (line 309)
|
||||
- Function: `task_change_client()` (line 320)
|
||||
- Function: `task_change_priority()` (line 334)
|
||||
- Function: `task_change_status()` (line 348)
|
||||
- Function: `task_end()` (line 353)
|
||||
- Function: `task_start()` (line 364)
|
||||
- Function: `task_edit()` (line 375)
|
||||
- Function: `task_save()` (line 398)
|
||||
- Function: `task_popup()` (line 423)
|
||||
- Function: `task_attachment_upload()` (line 449)
|
||||
- Function: `normalize_uploads_array()` (line 496)
|
||||
- Function: `task_attachment_delete()` (line 519)
|
||||
- Function: `task_attachment_rename()` (line 539)
|
||||
- Function: `filtr_save_form()` (line 559)
|
||||
- Function: `filtr_save()` (line 571)
|
||||
- Function: `filtr_update()` (line 589)
|
||||
- Function: `work_time()` (line 609)
|
||||
- Function: `change_task_work_date_start()` (line 614)
|
||||
- Function: `change_task_work_date_end()` (line 619)
|
||||
- Function: `work_delete()` (line 624)
|
||||
- Function: `filtr_set_default()` (line 633)
|
||||
- Function: `filtr_get()` (line 650)
|
||||
|
||||
### `autoload/controls/class.Users.php`
|
||||
- Class: `Users` (line 4)
|
||||
- Function: `permissions()` (line 7)
|
||||
- Function: `logout()` (line 46)
|
||||
- Function: `settings_save()` (line 57)
|
||||
- Function: `settings()` (line 71)
|
||||
- Function: `login()` (line 85)
|
||||
- Function: `login_form()` (line 117)
|
||||
|
||||
### `autoload/controls/class.Wiki.php`
|
||||
- Class: `Wiki` (line 4)
|
||||
- Function: `category_delete()` (line 7)
|
||||
- Function: `category_save()` (line 20)
|
||||
- Function: `category_edit()` (line 34)
|
||||
- Function: `category_preview()` (line 47)
|
||||
- Function: `main_view()` (line 59)
|
||||
|
||||
### `autoload/Domain/Tasks/MailToTaskImporter.php`
|
||||
- Class: `MailToTaskImporter` (line 4)
|
||||
- Function: `__construct()` (line 14)
|
||||
- Function: `importFromImap()` (line 27)
|
||||
- Function: `buildMailbox()` (line 197)
|
||||
- Function: `resolveClientIdBySenderDomain()` (line 214)
|
||||
- Function: `parseEmailsField()` (line 241)
|
||||
- Function: `extractDomainFromEmail()` (line 254)
|
||||
- Function: `parseReceivedDate()` (line 265)
|
||||
- Function: `extractSender()` (line 274)
|
||||
- Function: `decodeHeaderValue()` (line 289)
|
||||
- Function: `messageKey()` (line 306)
|
||||
- Function: `isMessageFinalized()` (line 318)
|
||||
- Function: `getImportStatus()` (line 324)
|
||||
- Function: `saveImportLog()` (line 329)
|
||||
- Function: `ensureImportTable()` (line 346)
|
||||
- Function: `extractMessageContent()` (line 365)
|
||||
- Function: `flattenParts()` (line 410)
|
||||
- Function: `parseSinglePart()` (line 441)
|
||||
- Function: `partParams()` (line 485)
|
||||
- Function: `decodePartBody()` (line 510)
|
||||
- Function: `mimeType()` (line 521)
|
||||
- Function: `htmlToText()` (line 540)
|
||||
- Function: `cleanBodyText()` (line 557)
|
||||
- Function: `prepareImportedTaskText()` (line 601)
|
||||
- Function: `shouldImportAttachment()` (line 622)
|
||||
- Function: `extractReferencedCidValues()` (line 647)
|
||||
- Function: `normalizeContentId()` (line 668)
|
||||
- Function: `parseWithAI()` (line 678)
|
||||
|
||||
### `autoload/Domain/Tasks/TaskAttachmentRepository.php`
|
||||
- Class: `TaskAttachmentRepository` (line 4)
|
||||
- Function: `__construct()` (line 11)
|
||||
- Function: `listByTaskId()` (line 25)
|
||||
- Function: `upload()` (line 46)
|
||||
- Function: `uploadFromContent()` (line 76)
|
||||
- Function: `rename()` (line 100)
|
||||
- Function: `delete()` (line 112)
|
||||
- Function: `purgeByTaskId()` (line 137)
|
||||
- Function: `effectiveTitle()` (line 159)
|
||||
- Function: `sanitizeFileName()` (line 165)
|
||||
- Function: `ensureStorage()` (line 173)
|
||||
- Function: `ensureTable()` (line 185)
|
||||
- Function: `storeMeta()` (line 209)
|
||||
- Function: `buildPublicUrl()` (line 230)
|
||||
- Function: `formatSize()` (line 235)
|
||||
- Function: `resolveFilePath()` (line 247)
|
||||
|
||||
### `autoload/Domain/Tasks/WorkTimeRepository.php`
|
||||
- Class: `WorkTimeRepository` (line 4)
|
||||
- Function: `__construct()` (line 9)
|
||||
- Function: `getClientsWithUnsettledTasks()` (line 20)
|
||||
- Function: `buildClientTasksByMonth()` (line 44)
|
||||
- Function: `getClientTaskRows()` (line 74)
|
||||
- Function: `getUnsettledTaskStatuses()` (line 91)
|
||||
- Function: `getTaskTotalTimeByMonth()` (line 96)
|
||||
|
||||
### `autoload/factory/class.BackendSites.php`
|
||||
- Class: `BackendSites` (line 3)
|
||||
- Function: `topic_delete()` (line 6)
|
||||
- Function: `topic_unaccept()` (line 12)
|
||||
- Function: `topic_accept()` (line 18)
|
||||
- Function: `topic_save()` (line 24)
|
||||
- Function: `topic()` (line 57)
|
||||
- Function: `collective_topic()` (line 63)
|
||||
- Function: `collective_topic_save()` (line 69)
|
||||
|
||||
### `autoload/factory/class.Crm.php`
|
||||
- Class: `Crm` (line 14)
|
||||
- Function: `settings()` (line 19)
|
||||
- Function: `get_client_name()` (line 31)
|
||||
- Function: `get_client_list()` (line 36)
|
||||
- Function: `client_delete()` (line 41)
|
||||
- Function: `client_details()` (line 47)
|
||||
- Function: `client_save()` (line 52)
|
||||
|
||||
### `autoload/factory/class.Cron.php`
|
||||
- Class: `Cron` (line 3)
|
||||
- Function: `remove_points_history()` (line 5)
|
||||
- Function: `update_points()` (line 18)
|
||||
- Function: `send_push()` (line 31)
|
||||
- Function: `send_emails()` (line 291)
|
||||
|
||||
### `autoload/factory/class.Finances.php`
|
||||
- Class: `Finances` (line 3)
|
||||
- Function: `first_operation_date()` (line 5)
|
||||
- Function: `get_operation_tags()` (line 11)
|
||||
- Function: `client_name()` (line 25)
|
||||
- Function: `clients_list_by_dates()` (line 31)
|
||||
- Function: `clients_list()` (line 37)
|
||||
- Function: `category_delete()` (line 43)
|
||||
- Function: `default_group()` (line 48)
|
||||
- Function: `groups_list()` (line 54)
|
||||
- Function: `operation_delete()` (line 60)
|
||||
- Function: `tags_json()` (line 66)
|
||||
- Function: `tags_list()` (line 72)
|
||||
- Function: `operations_list()` (line 91)
|
||||
- Function: `operation_details()` (line 105)
|
||||
- Function: `operation_save()` (line 115)
|
||||
- Function: `category_details()` (line 171)
|
||||
- Function: `category_save()` (line 177)
|
||||
- Function: `wallet_expenses_this_month()` (line 203)
|
||||
- Function: `wallet_income_this_month()` (line 214)
|
||||
- Function: `wallet_summary_this_month()` (line 225)
|
||||
- Function: `wallet_summary()` (line 236)
|
||||
- Function: `operations()` (line 244)
|
||||
- Function: `categories()` (line 273)
|
||||
|
||||
### `autoload/factory/class.Projects.php`
|
||||
- Class: `Projects` (line 3)
|
||||
- Function: `projects_list()` (line 5)
|
||||
- Function: `count_open_subtasks()` (line 12)
|
||||
- Function: `task_text_new()` (line 18)
|
||||
- Function: `task_text_update()` (line 29)
|
||||
- Function: `task_total_time()` (line 40)
|
||||
- Function: `send_email_task_change_status()` (line 63)
|
||||
- Function: `task_order_save()` (line 90)
|
||||
- Function: `action_mark_as_done()` (line 104)
|
||||
- Function: `action_name()` (line 112)
|
||||
- Function: `send_email_notification()` (line 118)
|
||||
- Function: `get_task_name()` (line 127)
|
||||
- Function: `set_project_as_default()` (line 133)
|
||||
- Function: `task_update()` (line 139)
|
||||
- Function: `project_delete()` (line 151)
|
||||
- Function: `project_name()` (line 157)
|
||||
- Function: `open_task()` (line 163)
|
||||
- Function: `task_change_status()` (line 176)
|
||||
- Function: `task_delete()` (line 247)
|
||||
- Function: `project_save()` (line 257)
|
||||
- Function: `project_user_id()` (line 329)
|
||||
- Function: `task_user_id()` (line 335)
|
||||
- Function: `project_details()` (line 341)
|
||||
- Function: `tasks_without_project()` (line 351)
|
||||
- Function: `get_project_name()` (line 365)
|
||||
- Function: `user_projects()` (line 371)
|
||||
- Function: `get_unassigned_tasks()` (line 402)
|
||||
- Function: `get_closed_tasks()` (line 431)
|
||||
- Function: `get_toreview_tasks()` (line 476)
|
||||
- Function: `get_inprogress_tasks()` (line 509)
|
||||
- Function: `user_tasks()` (line 555)
|
||||
|
||||
### `autoload/factory/class.Tasks.php`
|
||||
- Class: `Tasks` (line 4)
|
||||
- Function: `filtr_details()` (line 11)
|
||||
- Function: `get_priorities()` (line 17)
|
||||
- Function: `task_change_dates()` (line 22)
|
||||
- Function: `parent_tasks()` (line 37)
|
||||
- Function: `get_tasks_gantt()` (line 59)
|
||||
- Function: `work_delete()` (line 117)
|
||||
- Function: `change_task_work_date_end()` (line 122)
|
||||
- Function: `change_task_work_date_start()` (line 128)
|
||||
- Function: `task_works()` (line 134)
|
||||
- Function: `get_statuses()` (line 140)
|
||||
- Function: `clear_task_opened()` (line 150)
|
||||
- Function: `set_task_opened_by_user()` (line 156)
|
||||
- Function: `is_taks_is_opened_by_user()` (line 169)
|
||||
- Function: `get_filtrs()` (line 175)
|
||||
- Function: `filtr_update()` (line 180)
|
||||
- Function: `filtr_save()` (line 190)
|
||||
- Function: `action_change_status()` (line 208)
|
||||
- Function: `comment_delete()` (line 213)
|
||||
- Function: `comment_save()` (line 218)
|
||||
- Function: `action_delete()` (line 233)
|
||||
- Function: `action_save()` (line 239)
|
||||
- Function: `get_tasks()` (line 248)
|
||||
- Function: `get_open_task_id()` (line 295)
|
||||
- Function: `task_start()` (line 301)
|
||||
- Function: `task_end()` (line 340)
|
||||
- Function: `is_work_duration_too_short()` (line 368)
|
||||
- Function: `task_details()` (line 379)
|
||||
- Function: `task_total_time()` (line 394)
|
||||
- Function: `is_task_open()` (line 417)
|
||||
- Function: `work_time_clients()` (line 427)
|
||||
- Function: `task_save()` (line 434)
|
||||
- Function: `task_delete()` (line 532)
|
||||
- Function: `task_first_id()` (line 540)
|
||||
- Function: `task_delete_all()` (line 549)
|
||||
- Function: `task_delete_from_db()` (line 567)
|
||||
- Function: `filtr_set_default()` (line 582)
|
||||
- Function: `get_default_filtr()` (line 590)
|
||||
|
||||
### `autoload/factory/class.Users.php`
|
||||
- Class: `Users` (line 3)
|
||||
- Function: `user_details()` (line 5)
|
||||
- Function: `get_default_project()` (line 18)
|
||||
- Function: `get_user_email()` (line 24)
|
||||
- Function: `user_name()` (line 30)
|
||||
- Function: `users_list()` (line 39)
|
||||
- Function: `settings_save()` (line 62)
|
||||
- Function: `login()` (line 73)
|
||||
|
||||
### `autoload/factory/class.Wiki.php`
|
||||
- Class: `Wiki` (line 4)
|
||||
- Function: `category_delete()` (line 6)
|
||||
- Function: `category_save()` (line 11)
|
||||
- Function: `category_details()` (line 45)
|
||||
- Function: `category_users()` (line 52)
|
||||
- Function: `get_categories()` (line 58)
|
||||
|
||||
### `autoload/view/class.Cron.php`
|
||||
- Class: `Cron` (line 4)
|
||||
- Function: `main_view()` (line 6)
|
||||
|
||||
### `autoload/view/class.Projects.php`
|
||||
- Class: `Projects` (line 3)
|
||||
|
||||
### `autoload/view/class.Site.php`
|
||||
- Class: `Site` (line 3)
|
||||
- Function: `show()` (line 5)
|
||||
|
||||
### `autoload/view/class.Users.php`
|
||||
- Class: `Users` (line 3)
|
||||
- Function: `points_history()` (line 5)
|
||||
- Function: `settings()` (line 12)
|
||||
|
||||
### `ceidg.php`
|
||||
- Function: `__autoload_my_classes()` (line 3)
|
||||
- Function: `memory_get_process_usage()` (line 114)
|
||||
|
||||
### `cron.php`
|
||||
- Function: `__autoload_my_classes()` (line 4)
|
||||
|
||||
### `index.php`
|
||||
- Function: `__autoload_my_classes()` (line 3)
|
||||
|
||||
### `tests/Controllers/TasksControllerTest.php`
|
||||
- Function: `assert_same()` (line 7)
|
||||
- Function: `run_tasks_controller_tests()` (line 13)
|
||||
|
||||
### `tests/Domain/Tasks/TaskAttachmentRepositoryTest.php`
|
||||
- Function: `run_task_attachment_repository_tests()` (line 7)
|
||||
|
||||
### `tests/Domain/Tasks/WorkTimeRepositoryTest.php`
|
||||
- Function: `assert_true()` (line 7)
|
||||
- Function: `run_work_time_repository_tests()` (line 13)
|
||||
|
||||
133
autoload/Controllers/UsersController.php
Normal file
133
autoload/Controllers/UsersController.php
Normal file
@@ -0,0 +1,133 @@
|
||||
<?php
|
||||
namespace Controllers;
|
||||
|
||||
class UsersController
|
||||
{
|
||||
private const ADMIN_USER_ID = 1;
|
||||
private const IMPERSONATOR_SESSION_KEY = 'impersonator_user';
|
||||
|
||||
public static function mainView()
|
||||
{
|
||||
global $user;
|
||||
|
||||
if ( !$user )
|
||||
return \controls\Users::login_form();
|
||||
|
||||
$impersonator_user = self::getImpersonatorUser();
|
||||
if ( !self::canManageUsers( $user, $impersonator_user ) )
|
||||
self::forbiddenRedirect();
|
||||
|
||||
$users_repository = new \Domain\Users\UserRepository();
|
||||
|
||||
return \Tpl::view( 'users/main-view', self::buildMainViewModel(
|
||||
$user,
|
||||
$impersonator_user,
|
||||
$users_repository -> all()
|
||||
) );
|
||||
}
|
||||
|
||||
public static function loginAs()
|
||||
{
|
||||
global $user;
|
||||
|
||||
if ( !$user )
|
||||
return \controls\Users::login_form();
|
||||
|
||||
$impersonator_user = self::getImpersonatorUser();
|
||||
if ( !self::canManageUsers( $user, $impersonator_user ) )
|
||||
self::forbiddenRedirect();
|
||||
|
||||
$target_user_id = (int)\S::get( 'user_id' );
|
||||
$users_repository = new \Domain\Users\UserRepository();
|
||||
$target_user = $users_repository -> byId( $target_user_id );
|
||||
|
||||
if ( !$target_user )
|
||||
{
|
||||
\S::alert( 'Nie znaleziono wskazanego uzytkownika.' );
|
||||
header( 'Location: /users/main_view/' );
|
||||
exit;
|
||||
}
|
||||
|
||||
$new_session_state = self::impersonationStateAfterLoginAs( $user, $target_user, $impersonator_user );
|
||||
|
||||
\S::set_session( 'user', $new_session_state['user'] );
|
||||
\S::set_session( self::IMPERSONATOR_SESSION_KEY, $new_session_state['impersonator_user'] );
|
||||
|
||||
\S::alert( 'Zalogowano jako: ' . $target_user['name'] . ' ' . $target_user['surname'] . '.' );
|
||||
header( 'Location: /' );
|
||||
exit;
|
||||
}
|
||||
|
||||
public static function switchBackToAdmin()
|
||||
{
|
||||
$impersonator_user = self::getImpersonatorUser();
|
||||
|
||||
if ( !$impersonator_user or !isset( $impersonator_user['id'] ) or (int)$impersonator_user['id'] !== self::ADMIN_USER_ID )
|
||||
{
|
||||
\S::alert( 'Brak aktywnej sesji podszywania.' );
|
||||
header( 'Location: /' );
|
||||
exit;
|
||||
}
|
||||
|
||||
\S::set_session( 'user', $impersonator_user );
|
||||
\S::del_session( self::IMPERSONATOR_SESSION_KEY );
|
||||
|
||||
\S::alert( 'Powrot do konta administratora.' );
|
||||
header( 'Location: /users/main_view/' );
|
||||
exit;
|
||||
}
|
||||
|
||||
public static function canManageUsers( $current_user, $impersonator_user = null )
|
||||
{
|
||||
if ( !is_array( $current_user ) )
|
||||
return false;
|
||||
|
||||
if ( isset( $current_user['id'] ) and (int)$current_user['id'] === self::ADMIN_USER_ID )
|
||||
return true;
|
||||
|
||||
if ( is_array( $impersonator_user ) and isset( $impersonator_user['id'] ) and (int)$impersonator_user['id'] === self::ADMIN_USER_ID )
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static function buildMainViewModel( $current_user, $impersonator_user, array $users )
|
||||
{
|
||||
return [
|
||||
'current_user' => $current_user,
|
||||
'impersonator_user' => $impersonator_user,
|
||||
'users' => $users,
|
||||
'can_switch_back' => is_array( $impersonator_user ) and isset( $impersonator_user['id'] ) and (int)$impersonator_user['id'] === self::ADMIN_USER_ID
|
||||
];
|
||||
}
|
||||
|
||||
public static function impersonationStateAfterLoginAs( $current_user, $target_user, $existing_impersonator_user = null )
|
||||
{
|
||||
$impersonator_user = $existing_impersonator_user;
|
||||
|
||||
if ( !is_array( $impersonator_user ) )
|
||||
$impersonator_user = ( is_array( $current_user ) and isset( $current_user['id'] ) and (int)$current_user['id'] === self::ADMIN_USER_ID ) ? $current_user : null;
|
||||
|
||||
return [
|
||||
'user' => $target_user,
|
||||
'impersonator_user' => $impersonator_user
|
||||
];
|
||||
}
|
||||
|
||||
private static function getImpersonatorUser()
|
||||
{
|
||||
$session_value = \S::get_session( self::IMPERSONATOR_SESSION_KEY );
|
||||
|
||||
if ( is_array( $session_value ) )
|
||||
return $session_value;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static function forbiddenRedirect()
|
||||
{
|
||||
\S::alert( 'Brak uprawnien do zarzadzania uzytkownikami.' );
|
||||
header( 'Location: /' );
|
||||
exit;
|
||||
}
|
||||
}
|
||||
33
autoload/Domain/Users/UserRepository.php
Normal file
33
autoload/Domain/Users/UserRepository.php
Normal file
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
namespace Domain\Users;
|
||||
|
||||
class UserRepository
|
||||
{
|
||||
private $mdb;
|
||||
|
||||
public function __construct( $mdb = null )
|
||||
{
|
||||
if ( $mdb )
|
||||
$this -> mdb = $mdb;
|
||||
else if ( isset( $GLOBALS['mdb'] ) )
|
||||
$this -> mdb = $GLOBALS['mdb'];
|
||||
else
|
||||
$this -> mdb = null;
|
||||
}
|
||||
|
||||
public function all()
|
||||
{
|
||||
if ( !$this -> mdb )
|
||||
return [];
|
||||
|
||||
return $this -> mdb -> select( 'users', '*', [ 'ORDER' => [ 'id' => 'ASC' ] ] );
|
||||
}
|
||||
|
||||
public function byId( $user_id )
|
||||
{
|
||||
if ( !$this -> mdb )
|
||||
return false;
|
||||
|
||||
return $this -> mdb -> get( 'users', '*', [ 'id' => (int)$user_id ] );
|
||||
}
|
||||
}
|
||||
@@ -119,4 +119,28 @@ class Users
|
||||
return \Tpl::view( 'users/login-form' );
|
||||
}
|
||||
|
||||
}
|
||||
/**
|
||||
* @deprecated Use \Controllers\UsersController::mainView() instead.
|
||||
*/
|
||||
public static function main_view()
|
||||
{
|
||||
return \Controllers\UsersController::mainView();
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use \Controllers\UsersController::loginAs() instead.
|
||||
*/
|
||||
public static function login_as()
|
||||
{
|
||||
return \Controllers\UsersController::loginAs();
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use \Controllers\UsersController::switchBackToAdmin() instead.
|
||||
*/
|
||||
public static function back_to_admin()
|
||||
{
|
||||
return \Controllers\UsersController::switchBackToAdmin();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
namespace factory;
|
||||
class Users
|
||||
{
|
||||
|
||||
@@ -39,6 +39,13 @@
|
||||
<i class="fa fa-user"></i> <span><?= $this -> user[ 'email' ];?></span>
|
||||
</div>
|
||||
<ul>
|
||||
<? $impersonator_user = \S::get_session( 'impersonator_user' );?>
|
||||
<? if ( is_array( $impersonator_user ) and isset( $impersonator_user['id'] ) and (int)$impersonator_user['id'] === 1 ):?>
|
||||
<li>
|
||||
<a href="/users/back_to_admin/">Powrot do admina</a>
|
||||
</li>
|
||||
<li id="divider"></li>
|
||||
<? endif;?>
|
||||
<li>
|
||||
<a href="/users/settings/">Ustawienia</a>
|
||||
</li>
|
||||
@@ -51,6 +58,7 @@
|
||||
</div>
|
||||
<div class="main-menu">
|
||||
<ul>
|
||||
<? $can_manage_users = (int)$this -> user['id'] === 1;?>
|
||||
<? if ( \controls\Users::permissions( $this -> user[ 'id' ], 'projects' ) ):?>
|
||||
<li>
|
||||
<a href="/tasks/main_view/">Zadania</a>
|
||||
@@ -81,6 +89,11 @@
|
||||
<a href="/wiki/main_view/">Wiki</a>
|
||||
</li>
|
||||
<? endif;?>
|
||||
<? if ( $can_manage_users ):?>
|
||||
<li>
|
||||
<a href="/users/main_view/">Użytkownicy</a>
|
||||
</li>
|
||||
<? endif;?>
|
||||
<? if ( \controls\Users::permissions( $this -> user[ 'id' ], 'zaplecze' ) ):?>
|
||||
<li>
|
||||
<a href="#">Zaplecze</a>
|
||||
|
||||
@@ -45,6 +45,13 @@
|
||||
<i class="fa fa-user"></i> <span><?= $this -> user[ 'email' ];?></span>
|
||||
</div>
|
||||
<ul>
|
||||
<? $impersonator_user = \S::get_session( 'impersonator_user' );?>
|
||||
<? if ( is_array( $impersonator_user ) and isset( $impersonator_user['id'] ) and (int)$impersonator_user['id'] === 1 ):?>
|
||||
<li>
|
||||
<a href="/users/back_to_admin/">Powrot do admina</a>
|
||||
</li>
|
||||
<li id="divider"></li>
|
||||
<? endif;?>
|
||||
<li>
|
||||
<a href="/users/settings/">Ustawienia</a>
|
||||
</li>
|
||||
@@ -57,6 +64,7 @@
|
||||
</div>
|
||||
<div class="main-menu">
|
||||
<ul>
|
||||
<? $can_manage_users = (int)$this -> user['id'] === 1;?>
|
||||
<? if ( \controls\Users::permissions( $this -> user[ 'id' ], 'tasks' ) ):?>
|
||||
<li>
|
||||
<a href="/tasks/main_view/">Zadania</a>
|
||||
@@ -87,6 +95,11 @@
|
||||
<a href="/wiki/main_view/">Wiki</a>
|
||||
</li>
|
||||
<? endif;?>
|
||||
<? if ( $can_manage_users ):?>
|
||||
<li>
|
||||
<a href="/users/main_view/">Użytkownicy</a>
|
||||
</li>
|
||||
<? endif;?>
|
||||
<? if ( \controls\Users::permissions( $this -> user[ 'id' ], 'zaplecze' ) ):?>
|
||||
<li>
|
||||
<a href="/backend_sites/collective_topics/">Zaplecze - tematy zbiorcze</a>
|
||||
|
||||
55
templates/users/main-view.php
Normal file
55
templates/users/main-view.php
Normal file
@@ -0,0 +1,55 @@
|
||||
<div class="form_container full">
|
||||
<div class="block-header">
|
||||
<h2>Zarządzanie <strong>użytkownikami</strong></h2>
|
||||
</div>
|
||||
|
||||
<div class="action_menu">
|
||||
<? if ( $this -> can_switch_back ):?>
|
||||
<a href="/users/back_to_admin/" class="btn btn-warning" title="Powrót do konta administratora">
|
||||
<i class="fa fa-undo"></i> Powrot do admina
|
||||
</a>
|
||||
<? endif;?>
|
||||
</div>
|
||||
|
||||
<div class="content">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 60px;">ID</th>
|
||||
<th>Imię i nazwisko</th>
|
||||
<th>Email</th>
|
||||
<th style="width: 240px;">Akcje</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<? if ( is_array( $this -> users ) and count( $this -> users ) ): foreach ( $this -> users as $user_tmp ):?>
|
||||
<? $is_current = (int)$this -> current_user['id'] === (int)$user_tmp['id'];?>
|
||||
<tr>
|
||||
<td class="center"><?= (int)$user_tmp['id'];?></td>
|
||||
<td class="left">
|
||||
<?= htmlspecialchars( trim( $user_tmp['name'] . ' ' . $user_tmp['surname'] ) );?>
|
||||
<? if ( (int)$user_tmp['id'] === 1 ):?>
|
||||
<span class="label label-info" style="margin-left: 8px;">ADMIN</span>
|
||||
<? endif;?>
|
||||
</td>
|
||||
<td class="left"><?= htmlspecialchars( $user_tmp['email'] );?></td>
|
||||
<td class="center">
|
||||
<? if ( $is_current ):?>
|
||||
<span class="btn btn-default btn_small disabled">Aktywna sesja</span>
|
||||
<? else:?>
|
||||
<a href="/users/login_as/user_id=<?= (int)$user_tmp['id'];?>" class="btn btn-primary btn_small">
|
||||
<i class="fa fa-sign-in"></i>
|
||||
Zaloguj jako
|
||||
</a>
|
||||
<? endif;?>
|
||||
</td>
|
||||
</tr>
|
||||
<? endforeach; else:?>
|
||||
<tr>
|
||||
<td colspan="4" class="center">Brak użytkowników.</td>
|
||||
</tr>
|
||||
<? endif;?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
32
tests/Controllers/UsersControllerTest.php
Normal file
32
tests/Controllers/UsersControllerTest.php
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
require_once __DIR__ . '/../../autoload/Controllers/UsersController.php';
|
||||
|
||||
use Controllers\UsersController;
|
||||
|
||||
function assert_users_controller_same( $expected, $actual, $message )
|
||||
{
|
||||
if ( $expected !== $actual )
|
||||
throw new Exception( $message );
|
||||
}
|
||||
|
||||
function run_users_controller_tests()
|
||||
{
|
||||
$admin_user = [ 'id' => 1, 'name' => 'Admin', 'surname' => 'One' ];
|
||||
$regular_user = [ 'id' => 3, 'name' => 'Jan', 'surname' => 'Kowalski' ];
|
||||
$target_user = [ 'id' => 5, 'name' => 'Anna', 'surname' => 'Nowak' ];
|
||||
|
||||
assert_users_controller_same( true, UsersController::canManageUsers( $admin_user, null ), 'Expected admin to manage users.' );
|
||||
assert_users_controller_same( false, UsersController::canManageUsers( $regular_user, null ), 'Expected regular user to not manage users.' );
|
||||
assert_users_controller_same( true, UsersController::canManageUsers( $regular_user, $admin_user ), 'Expected impersonated admin session to manage users.' );
|
||||
|
||||
$state = UsersController::impersonationStateAfterLoginAs( $admin_user, $target_user, null );
|
||||
assert_users_controller_same( 5, (int)$state['user']['id'], 'Expected impersonation to switch current user to target user.' );
|
||||
assert_users_controller_same( 1, (int)$state['impersonator_user']['id'], 'Expected impersonator to be preserved as admin user.' );
|
||||
|
||||
$state_with_existing = UsersController::impersonationStateAfterLoginAs( $regular_user, $target_user, $admin_user );
|
||||
assert_users_controller_same( 1, (int)$state_with_existing['impersonator_user']['id'], 'Expected existing impersonator to stay unchanged.' );
|
||||
|
||||
$view_model = UsersController::buildMainViewModel( $target_user, $admin_user, [ $admin_user, $regular_user, $target_user ] );
|
||||
assert_users_controller_same( true, $view_model['can_switch_back'], 'Expected can_switch_back to be true when impersonator is admin.' );
|
||||
}
|
||||
53
tests/Domain/Users/UserRepositoryTest.php
Normal file
53
tests/Domain/Users/UserRepositoryTest.php
Normal file
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
require_once __DIR__ . '/../../../autoload/Domain/Users/UserRepository.php';
|
||||
|
||||
use Domain\Users\UserRepository;
|
||||
|
||||
class FakeUsersMdb
|
||||
{
|
||||
public $last_select = [];
|
||||
public $last_get = [];
|
||||
private $rows = [];
|
||||
|
||||
public function __construct( array $rows )
|
||||
{
|
||||
$this -> rows = $rows;
|
||||
}
|
||||
|
||||
public function select( $table, $columns, $where )
|
||||
{
|
||||
$this -> last_select = [ 'table' => $table, 'columns' => $columns, 'where' => $where ];
|
||||
return $this -> rows;
|
||||
}
|
||||
|
||||
public function get( $table, $columns, $where )
|
||||
{
|
||||
$this -> last_get = [ 'table' => $table, 'columns' => $columns, 'where' => $where ];
|
||||
foreach ( $this -> rows as $row )
|
||||
{
|
||||
if ( (int)$row['id'] === (int)$where['id'] )
|
||||
return $row;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function run_user_repository_tests()
|
||||
{
|
||||
$rows = [
|
||||
[ 'id' => 1, 'email' => 'admin@example.com', 'name' => 'Admin', 'surname' => 'User' ],
|
||||
[ 'id' => 3, 'email' => 'user@example.com', 'name' => 'Normal', 'surname' => 'User' ]
|
||||
];
|
||||
|
||||
$fake_mdb = new FakeUsersMdb( $rows );
|
||||
$repository = new UserRepository( $fake_mdb );
|
||||
|
||||
$all = $repository -> all();
|
||||
assert_true( count( $all ) === 2, 'Expected all() to return all users.' );
|
||||
assert_true( $fake_mdb -> last_select['table'] === 'users', 'Expected all() to query users table.' );
|
||||
|
||||
$user = $repository -> byId( 3 );
|
||||
assert_true( (int)$user['id'] === 3, 'Expected byId() to return matching user.' );
|
||||
assert_true( (int)$fake_mdb -> last_get['where']['id'] === 3, 'Expected byId() to query requested id.' );
|
||||
}
|
||||
@@ -2,12 +2,16 @@
|
||||
|
||||
require_once __DIR__ . '/Domain/Tasks/WorkTimeRepositoryTest.php';
|
||||
require_once __DIR__ . '/Domain/Tasks/TaskAttachmentRepositoryTest.php';
|
||||
require_once __DIR__ . '/Domain/Users/UserRepositoryTest.php';
|
||||
require_once __DIR__ . '/Controllers/TasksControllerTest.php';
|
||||
require_once __DIR__ . '/Controllers/UsersControllerTest.php';
|
||||
|
||||
$tests = [
|
||||
'run_work_time_repository_tests',
|
||||
'run_task_attachment_repository_tests',
|
||||
'run_tasks_controller_tests'
|
||||
'run_user_repository_tests',
|
||||
'run_tasks_controller_tests',
|
||||
'run_users_controller_tests'
|
||||
];
|
||||
|
||||
$failed = 0;
|
||||
|
||||
Reference in New Issue
Block a user