diff --git a/.vscode/ftp-kr.sync.cache.json b/.vscode/ftp-kr.sync.cache.json index 7e50840..e35e7e6 100644 --- a/.vscode/ftp-kr.sync.cache.json +++ b/.vscode/ftp-kr.sync.cache.json @@ -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": { diff --git a/CODE_INDEX.md b/CODE_INDEX.md new file mode 100644 index 0000000..70c0dff --- /dev/null +++ b/CODE_INDEX.md @@ -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) + diff --git a/autoload/Controllers/UsersController.php b/autoload/Controllers/UsersController.php new file mode 100644 index 0000000..aec2585 --- /dev/null +++ b/autoload/Controllers/UsersController.php @@ -0,0 +1,133 @@ + 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; + } +} diff --git a/autoload/Domain/Users/UserRepository.php b/autoload/Domain/Users/UserRepository.php new file mode 100644 index 0000000..c67efe5 --- /dev/null +++ b/autoload/Domain/Users/UserRepository.php @@ -0,0 +1,33 @@ + 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 ] ); + } +} diff --git a/autoload/controls/class.Users.php b/autoload/controls/class.Users.php index a4185c6..8ac4935 100644 --- a/autoload/controls/class.Users.php +++ b/autoload/controls/class.Users.php @@ -119,4 +119,28 @@ class Users return \Tpl::view( 'users/login-form' ); } -} \ No newline at end of file + /** + * @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(); + } + +} diff --git a/autoload/factory/class.Users.php b/autoload/factory/class.Users.php index 96a44df..69bbbc6 100644 --- a/autoload/factory/class.Users.php +++ b/autoload/factory/class.Users.php @@ -1,4 +1,5 @@ user[ 'email' ];?>