├── .gitignore ├── README.md ├── app ├── Handlers │ └── CustomExceptionHandler.php ├── Middlewares │ ├── ApiVerification.php │ └── CsrfVerifier.php ├── controllers │ ├── BaseController.php │ ├── GroupController.php │ ├── HomeController.php │ ├── IndexController.php │ ├── LoginController.php │ ├── LogoutController.php │ ├── UserController.php │ └── articleController.php ├── helpers.php ├── models │ ├── Article.php │ ├── DbHandler.php │ ├── Group.php │ ├── MySQLHandler.php │ └── User.php └── views │ ├── articles │ ├── create.php │ ├── index.php │ └── show.php │ ├── error.php │ ├── groups │ ├── create.php │ ├── edit.php │ ├── index.php │ └── show.php │ ├── home.php │ ├── index.php │ ├── login.php │ ├── partials │ ├── footer.php │ ├── header.php │ └── scripts.php │ └── users │ ├── create.php │ ├── edit.php │ ├── index.php │ ├── show.php │ └── showgroup.php ├── composer.json ├── composer.lock ├── log.php ├── package-lock.json ├── package.json ├── php_project.sql ├── public ├── .htaccess ├── css │ ├── custom.css │ ├── custom.min.css │ ├── error.css │ ├── groupIndex.css │ ├── groupsForm.css │ └── sidebar-accordion.css ├── error.log ├── images │ ├── ball.gif │ ├── ball.ico │ ├── blurred.jpg │ ├── default.png │ ├── error.png │ ├── favicon.ico │ ├── football.png │ ├── soccer-player.png │ ├── stad.jpg │ └── trophy.gif ├── index.php └── js │ ├── custom.js │ ├── custom.min.js │ └── sidebar-accordion.js └── routes └── web.php /.gitignore: -------------------------------------------------------------------------------- 1 | nbproject 2 | npm-debug.log 3 | node_modules 4 | .sass-cache 5 | vendor 6 | config.php 7 | public/images/articles/* 8 | /app/vendors/ 9 | public/error.log -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
15 | git clone https://github.com/asmaagamal871/php-project 16 |17 | 18 | ### Database creation 19 | - create database 20 | - import php_project.sql 21 | - create config.php 22 | 23 | 24 | ``` 25 | 37 | composer install 38 | 39 | 40 |
41 | composer dump-autoload 42 |43 | 44 | ### Run project 45 | 46 |
47 | cd public 48 |49 | 50 |
51 | php -S localhost:9000 52 |53 | 54 | ## Features 55 | 56 | - User authentication and Remember me option. 57 | - User profile. 58 | - Role-based access control. 59 | - Article creation, reading and deletion. 60 | - User creation, editting, reading and deletion. 61 | - Group creation, editting, reading and deletion. 62 | - Object soft delete. 63 | - Search and filtering. 64 | - Pagination of all listings. 65 | - Responsive design for mobile and desktop devices. 66 | - Chart statistics and analysis. 67 | - Error and exception logging 68 | 69 | ## Technologies 70 | - PHP 71 | - MySQL 72 | - JS 73 | - Bootstrap 74 | - CSS 75 | - HTML 76 | 77 | ## Packages 78 | - [Simple Router](https://github.com/skipperbent/simple-php-router) 79 | - [Chart.js](https://www.chartjs.org/) 80 | 81 | ## Roles 82 | 83 | | Role | Permission | 84 | | --- | --- | 85 | | Admin | Full access | 86 | | Editor | Full access on articles - View Groups | 87 | | User | Create and view their own articles | 88 | 89 | ## Authors 90 | 91 | - [Asmaa Gamal](https://github.com/asmaagamal871) 92 | 93 | - [Mayar Hamed](https://github.com/MayarHamed/) 94 | 95 | - [Shehab Zahran](https://github.com/Shehab8K) 96 | 97 | - [Nehad Osman](https://github.com/nehadosman) 98 | 99 | - [Sondos Saied](https://github.com/Sondos11) 100 | -------------------------------------------------------------------------------- /app/Handlers/CustomExceptionHandler.php: -------------------------------------------------------------------------------- 1 | getUrl()->contains('/api')) { 22 | 23 | response()->json([ 24 | 'error' => $error->getMessage(), 25 | 'code' => $error->getCode(), 26 | ]); 27 | 28 | } 29 | 30 | /* The router will throw the NotFoundHttpException on 404 */ 31 | if ($error instanceof NotFoundHttpException) { 32 | 33 | /* 34 | * Render your own custom 404-view, rewrite the request to another route, 35 | * or simply return the $request object to ignore the error and continue on rendering the route. 36 | * 37 | * The code below will make the router render our page.notfound route. 38 | */ 39 | 40 | $request->setRewriteCallback('DefaultController@notFound'); 41 | return; 42 | 43 | } 44 | 45 | throw $error; 46 | 47 | } 48 | 49 | } -------------------------------------------------------------------------------- /app/Middlewares/ApiVerification.php: -------------------------------------------------------------------------------- 1 | authenticated = true; 13 | } 14 | 15 | } -------------------------------------------------------------------------------- /app/Middlewares/CsrfVerifier.php: -------------------------------------------------------------------------------- 1 | get_record_by_id($_SESSION['group_id']); 14 | if ($group[0]['role'] == self::ADMIN_ROLE) { 15 | return true; 16 | } else 17 | return false; 18 | } 19 | } 20 | 21 | protected function isEditor() 22 | { 23 | if (isset($_SESSION['group_id'])) { 24 | $db = new MySQLHandler("groups"); 25 | $group = $db->get_record_by_id($_SESSION['group_id']); 26 | if ($group[0]['role'] == self::EDITOR_ROLE) { 27 | return true; 28 | } else 29 | return false; 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /app/controllers/GroupController.php: -------------------------------------------------------------------------------- 1 | getGroups(); 11 | 12 | if ($this->isAdmin() || $this->isEditor()) { 13 | include __DIR__ . '/../views/groups/index.php'; 14 | } else { 15 | $_SESSION['error'] = "Sorry, you are not an admin or editor"; 16 | header("Location: /"); 17 | } 18 | } 19 | 20 | public function show($id) 21 | { 22 | $group = new Group(); 23 | $result = $group->getByID($id); 24 | if ($this->isAdmin() || $this->isEditor()) { 25 | include __DIR__ . '/../views/groups/show.php'; 26 | } else { 27 | $_SESSION['error'] = "Sorry, you are not an admin or editor"; 28 | header("Location: /"); 29 | } 30 | } 31 | 32 | public function create() 33 | { 34 | if ($this->isAdmin()) { 35 | include __DIR__ . '/../views/groups/create.php'; 36 | } else { 37 | $_SESSION['error'] = "Sorry, you are not an admin"; 38 | header("Location: /groups"); 39 | } 40 | } 41 | 42 | public function store() 43 | { 44 | $check = $this->isAdmin(); 45 | $name = $_POST['name']; 46 | $pattern = '/^[A-Za-z]{4,}$/'; 47 | if (!preg_match($pattern, $name)) { 48 | $_SESSION['error'] = "Input must be at least 4 letters long and only contain letters."; 49 | header("Location: /groups/create"); 50 | exit(); 51 | } 52 | if ($check) { 53 | $group = new Group(); 54 | $create = $group->create(); 55 | if ($create) { 56 | header("Location: /groups"); 57 | exit; 58 | } else { 59 | $_SESSION['error'] = "Failed to Create, group name must be Unique"; 60 | header("Location: /groups/create"); 61 | } 62 | } else { 63 | $_SESSION['error'] = "Sorry, you are not an admin"; 64 | header("Location: /groups"); 65 | } 66 | } 67 | 68 | public function edit($id) 69 | { 70 | $check = $this->isAdmin(); 71 | if ($check) { 72 | $group = new Group(); 73 | $result = $group->getByID($id); 74 | include __DIR__ . '/../views/groups/edit.php'; 75 | } else { 76 | $_SESSION['error'] = "Sorry, you are not an admin"; 77 | header("Location: /groups"); 78 | } 79 | } 80 | 81 | public function update($id) 82 | { 83 | $check = $this->isAdmin(); 84 | $name = $_POST['name']; 85 | $pattern = '/^[A-Za-z]{4,}$/'; 86 | if (!preg_match($pattern, $name)) { 87 | $_SESSION['error'] = "Input must be at least 4 letters long and only contain letters."; 88 | header("Location: /groups/" . $_POST["id"] . "/edit"); 89 | exit(); 90 | } 91 | if ($check) { 92 | $group = new Group(); 93 | $update = $group->update($id); 94 | if ($update) { 95 | header("Location: /groups"); 96 | exit; 97 | } else { 98 | $_SESSION['error'] = "Failed to Update"; 99 | include __DIR__ . '/../views/groups/edit.php'; 100 | } 101 | } else { 102 | $_SESSION['error'] = "Sorry, you are not an admin"; 103 | header("Location: /groups"); 104 | } 105 | } 106 | 107 | public function destroy($id) 108 | { 109 | $check = $this->isAdmin(); 110 | if ($check) { 111 | $group = new Group(); 112 | $delete = $group->delete($id); 113 | if ($delete) { 114 | header("Location: /groups"); 115 | exit; 116 | } else { 117 | $_SESSION['error'] = "Failed to delete"; 118 | header("Location: /groups"); 119 | } 120 | } else { 121 | $_SESSION['error'] = "Sorry, you are not an admin"; 122 | header("Location: /groups"); 123 | } 124 | } 125 | 126 | public function restore($id) 127 | { 128 | $check = $this->isAdmin(); 129 | if ($check) { 130 | $group = new Group(); 131 | $restore = $group->restore($id); 132 | if ($restore) { 133 | header("Location: /groups"); 134 | exit; 135 | } else { 136 | $_SESSION['error'] = "Failed to restore"; 137 | header("Location: /groups"); 138 | } 139 | } else { 140 | $_SESSION['error'] = "Sorry, you are not an admin"; 141 | header("Location: /groups"); 142 | } 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /app/controllers/HomeController.php: -------------------------------------------------------------------------------- 1 | authenticate($email, $password)) { 30 | // $_SESSION['logged_in'] = true; 31 | if ($this->isAdmin()) 32 | $_SESSION['role'] = 'admin'; 33 | if ($this->isEditor()) 34 | $_SESSION['role'] = 'editor'; 35 | 36 | $_SESSION['last_login'] = date('Y-m-d H:i:s'); 37 | 38 | if (isset($_POST['remember_me'])&& $_POST['remember_me']) { 39 | // Set a cookies with the user's login credentials 40 | setcookie('email', $_POST['email'], time() + 86400 * 30); 41 | setcookie('password', $_POST['password'], time() + 86400 * 30); 42 | } 43 | 44 | if (isset($_COOKIE['email']) && isset($_COOKIE['password'])) { 45 | // Pre-fill the login form with the saved credentials 46 | $email = $_COOKIE['email']; 47 | $password = $_COOKIE['password']; 48 | } 49 | 50 | $data = array( 51 | "last_login" => $_SESSION['last_login'] 52 | ); 53 | 54 | $db->update($data, $_SESSION['user_id']); 55 | header('Location: /'); 56 | exit; 57 | 58 | } else { 59 | $_SESSION['error'] = "Invalid email or password!"; 60 | header('Location: /login'); 61 | exit; 62 | } 63 | } 64 | 65 | // Display the login form 66 | include '../views/login.php'; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /app/controllers/LogoutController.php: -------------------------------------------------------------------------------- 1 | getUsers(); 12 | include __DIR__ . '/../views/users/index.php'; 13 | } 14 | 15 | public function show($id) 16 | { 17 | $user = new User(); 18 | $result = $user->getByID($id); 19 | include __DIR__ . '/../views/users/show.php'; 20 | } 21 | 22 | public function create() 23 | { 24 | if ($this->isAdmin()) { 25 | 26 | include __DIR__ . '/../views/users/create.php'; 27 | } else { 28 | $_SESSION['error'] = "Sorry, you are not an admin"; 29 | header("Location: /users"); 30 | } 31 | } 32 | 33 | public function store() 34 | { 35 | $check = $this->isAdmin(); 36 | $email = $_POST['email']; 37 | $pattern_email = '/[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/'; 38 | $phone = $_POST['phone']; 39 | $pattern_phone = '/[0-9]{7,}/'; 40 | $username = $_POST['username']; 41 | $pattern_username = '/[a-zA-Z0-9]+/'; 42 | $name = $_POST['name']; 43 | $pattern_name = '/[a-zA-Z\s]+/'; 44 | $password = $_POST['password']; 45 | $pattern_password = '/[a-zA-Z0-9]{8,}$/'; 46 | 47 | 48 | if (!preg_match($pattern_email, $email)) { 49 | $_SESSION['error'] = "please enter a valid email format."; 50 | header("Location: /users/create"); 51 | exit(); 52 | } 53 | if (!preg_match($pattern_phone, $phone)) { 54 | $_SESSION['error'] = "phone number must be at least 7 digits and doesn't contain letters."; 55 | header("Location: /users/create"); 56 | exit(); 57 | } 58 | if (!preg_match($pattern_username, $username)) { 59 | $_SESSION['error'] = "username must be contain only letters and numbers."; 60 | header("Location: /users/create"); 61 | exit(); 62 | } 63 | if (!preg_match($pattern_name, $name)) { 64 | $_SESSION['error'] = "name must be contain only letters ."; 65 | header("Location: /users/create"); 66 | exit(); 67 | } 68 | if (!preg_match($pattern_password, $password)) { 69 | $_SESSION['error'] = "password must be contain at least 8 letters ."; 70 | header("Location: /users/create"); 71 | exit(); 72 | } 73 | 74 | if ($check) { 75 | $user = new User(); 76 | $create = $user->create(); 77 | if ($create) { 78 | header("Location: /users"); 79 | exit; 80 | } else { 81 | $_SESSION['error'] = "Failed to Create"; 82 | include __DIR__ . '/../views/users/create.php'; 83 | } 84 | } else { 85 | $_SESSION['error'] = "Sorry, you are not an admin"; 86 | header("Location: /userss"); 87 | } 88 | } 89 | 90 | public function edit($id) 91 | { 92 | $check = $this->isAdmin(); 93 | if ($check) { 94 | $user = new User(); 95 | $result = $user->getByID($id); 96 | include __DIR__ . '/../views/users/edit.php'; 97 | } else { 98 | $_SESSION['error'] = "Sorry, you are not an admin"; 99 | header("Location: /users"); 100 | } 101 | } 102 | 103 | public function update($id) 104 | { 105 | $check = $this->isAdmin(); 106 | $email = $_POST['email']; 107 | $pattern_email = '/[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/'; 108 | $phone = $_POST['phone']; 109 | $pattern_phone = '/[0-9]{7,}/'; 110 | $username = $_POST['username']; 111 | $pattern_username = '/[a-zA-Z0-9]+/'; 112 | $name = $_POST['name']; 113 | $pattern_name = '/[a-zA-Z\s]+/'; 114 | $password = $_POST['password']; 115 | $pattern_password = '/[a-zA-Z0-9]{8,}$/'; 116 | 117 | 118 | if (!preg_match($pattern_email, $email)) { 119 | $_SESSION['error'] = "please enter a valid email format."; 120 | header("Location: /users/create"); 121 | exit(); 122 | } 123 | if (!preg_match($pattern_phone, $phone)) { 124 | $_SESSION['error'] = "phone number must be at least 7 digits and doesn't contain letters."; 125 | header("Location: /users/create"); 126 | exit(); 127 | } 128 | if (!preg_match($pattern_username, $username)) { 129 | $_SESSION['error'] = "username must be contain only letters and numbers."; 130 | header("Location: /users/create"); 131 | exit(); 132 | } 133 | if (!preg_match($pattern_name, $name)) { 134 | $_SESSION['error'] = "name must be contain only letters ."; 135 | header("Location: /users/create"); 136 | exit(); 137 | } 138 | if (!preg_match($pattern_password, $password)) { 139 | $_SESSION['error'] = "password must be contain at least 8 letters ."; 140 | header("Location: /users/create"); 141 | exit(); 142 | } 143 | if ($check) { 144 | $user = new User(); 145 | $update = $user->update($id); 146 | if ($update) { 147 | header("Location: /users"); 148 | exit; 149 | } else { 150 | $_SESSION['error'] = "Failed to Update"; 151 | include __DIR__ . '/../views/users/edit.php'; 152 | } 153 | } else { 154 | $_SESSION['error'] = "Sorry, you are not an admin"; 155 | header("Location: /users"); 156 | } 157 | } 158 | 159 | public function destroy($id) 160 | { 161 | $check = $this->isAdmin(); 162 | if ($check) { 163 | $user = new User(); 164 | $delete = $user->delete($id); 165 | if ($delete) { 166 | header("Location: /users"); 167 | exit; 168 | } else { 169 | $_SESSION['error'] = "Failed to delete"; 170 | header("Location: /users"); 171 | } 172 | } else { 173 | $_SESSION['error'] = "Sorry, you are not an admin"; 174 | header("Location: /users"); 175 | } 176 | } 177 | 178 | public function restore($id) 179 | { 180 | $check = $this->isAdmin(); 181 | if ($check) { 182 | $user = new User(); 183 | $restore = $user->restore($id); 184 | if ($restore) { 185 | header("Location: /users"); 186 | exit; 187 | } else { 188 | $_SESSION['error'] = "Failed to restore"; 189 | header("Location: /users"); 190 | } 191 | } else { 192 | $_SESSION['error'] = "Sorry, you are not an admin"; 193 | header("Location: /users"); 194 | } 195 | } 196 | 197 | public function show_users_group($id) 198 | { 199 | $user = new User(); 200 | $users = $user->getByGroupID($id); 201 | include __DIR__ . '/../views/users/showgroup.php'; 202 | } 203 | } 204 | -------------------------------------------------------------------------------- /app/controllers/articleController.php: -------------------------------------------------------------------------------- 1 | isAdmin() || $this->isEditor()) { 13 | $articles = $article->getArticles(); 14 | } else { 15 | $articles = $article->getUserArticles($_SESSION['user_id']); 16 | } 17 | include __DIR__ . '/../views/articles/index.php'; 18 | } 19 | 20 | public function show($id) 21 | { 22 | $article = new Article; 23 | $res = $article->getByID($id); 24 | include __DIR__ . '/../views/articles/show.php'; 25 | } 26 | public function create() 27 | { 28 | include __DIR__ . '/../views/articles/create.php'; 29 | } 30 | 31 | public function store() 32 | { 33 | $article = new Article; 34 | 35 | $_POST["publish_date"] = date('Y-m-d'); 36 | $_POST["user_id"] = $_SESSION['user_id']; 37 | $ext = substr(strrchr($_FILES['image']['name'], '.'), 1);; 38 | $new_file_name = date('Y_m_d_H_i_s') . '.' . $ext; 39 | $target = __DIR__ . '/../../public/images/articles/'; 40 | move_uploaded_file($_FILES['image']['tmp_name'], $target . $new_file_name); 41 | $_POST["image"] = $new_file_name; 42 | $articles = $article->create($_POST); 43 | 44 | header("Location: /articles"); 45 | exit; 46 | } 47 | public function restore($id) 48 | { 49 | $check = $this->isAdmin(); 50 | if ($check) { 51 | $article = new Article; 52 | $restore = $article->restore($id); 53 | if ($restore) { 54 | header("Location: /articles"); 55 | exit; 56 | } else { 57 | $_SESSION['error'] = "Failed to restore"; 58 | header("Location: /articles"); 59 | } 60 | } else { 61 | $_SESSION['error'] = "Sorry, you are not an admin"; 62 | header("Location: /articles"); 63 | } 64 | } 65 | public function destroy($id) 66 | { 67 | $article = new Article; 68 | $delete = $article->delete($id); 69 | if ($delete) { 70 | header("Location: /articles"); 71 | exit; 72 | } else { 73 | include __DIR__ . '/../views/articles/index.php'; 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /app/helpers.php: -------------------------------------------------------------------------------- 1 | getInputHandler()->value($index, $defaultValue, ...$methods); 58 | } 59 | 60 | return request()->getInputHandler(); 61 | } 62 | 63 | /** 64 | * @param string $url 65 | * @param int|null $code 66 | */ 67 | function redirect(string $url, ?int $code = null): void 68 | { 69 | if ($code !== null) { 70 | response()->httpCode($code); 71 | } 72 | 73 | response()->redirect($url); 74 | } 75 | 76 | /** 77 | * Get current csrf-token 78 | * @return string|null 79 | */ 80 | function csrf_token(): ?string 81 | { 82 | $baseVerifier = Router::router()->getCsrfVerifier(); 83 | if ($baseVerifier !== null) { 84 | return $baseVerifier->getTokenProvider()->getToken(); 85 | } 86 | 87 | return null; 88 | } -------------------------------------------------------------------------------- /app/models/Article.php: -------------------------------------------------------------------------------- 1 | db = new MySQLHandler("articles"); 10 | } 11 | 12 | public function getArticles() 13 | { 14 | return $this->db->get_all_records_paginated(); 15 | } 16 | 17 | 18 | public function create() 19 | { 20 | return $this->db->save($_POST); 21 | } 22 | 23 | public function getByID($id) 24 | { 25 | return $this->db->get_record_by_id($id); 26 | } 27 | 28 | public function getUserArticles($id) 29 | { 30 | return $this->db->get_record_by_id($id, 'user_id'); 31 | } 32 | public function restore($id) 33 | { 34 | return $this->db->restore($id); 35 | } 36 | 37 | public function delete($id) 38 | { 39 | return $this->db->delete($id); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /app/models/DbHandler.php: -------------------------------------------------------------------------------- 1 | db = new MySQLHandler("groups"); 10 | } 11 | 12 | public function getGroups() 13 | { 14 | return $this->db->get_all(); 15 | } 16 | 17 | public function create() 18 | { 19 | try { 20 | $this->db->save($_POST); 21 | return true; 22 | } catch(Exception $e) { 23 | return false; 24 | } 25 | } 26 | 27 | public function getByID($id) 28 | { 29 | return $this->db->get_record_by_id($id); 30 | } 31 | 32 | public function update($id) 33 | { 34 | $data = array( 35 | "name" => $_POST["name"], 36 | "description" => $_POST["description"], 37 | ); 38 | 39 | try { 40 | $this->db->update($data, $id); 41 | return true; 42 | } catch(Exception $e) { 43 | return false; 44 | } 45 | } 46 | 47 | public function restore($id) 48 | { 49 | return $this->db->restore($id); 50 | } 51 | 52 | public function delete($id) 53 | { 54 | return $this->db->delete($id); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /app/models/MySQLHandler.php: -------------------------------------------------------------------------------- 1 | _table = $table; 16 | $this->connect(); 17 | $this->_primary_key = $primary_key; 18 | } 19 | public function connect() 20 | { 21 | try { 22 | $handler = mysqli_connect(__HOST__, __USER__, __PASS__, __DB__, __PORT__); 23 | if ($handler) { 24 | $this->_db_handler = $handler; 25 | return true; 26 | } else { 27 | return false; 28 | } 29 | } catch (Exception $e) { 30 | die($e); 31 | } 32 | } 33 | public function disconnect() 34 | { 35 | if ($this->_db_handler) { 36 | mysqli_close($this->_db_handler); 37 | } 38 | } 39 | 40 | private function get_results($sql) 41 | { 42 | if (__Debug__Mode__ === 1) { 43 | echo "
ID | 29 |Name | 30 |Description | 31 |Role | 32 |Action | 33 |" . $group["id"] . " | "; 41 | echo "" . $group["name"] . " | "; 42 | echo "" . $group["description"] . " | "; 43 | echo "" . $group["role"] . " | "; 44 | // echo '' . ($group['is_deleted'] ? 'Yes' : 'No') . ' | '; 45 | if ($group['is_deleted']) { 46 | echo ""; 47 | } else { 48 | // echo " 95 | |
---|
last login: 60 | 66 |
67 | 68 |ID | 29 |Name | 30 |Username | 31 |Phone | 33 |Group | 34 |" . $user["id"] . " | "; 42 | echo "" . $user["name"] . " | "; 43 | echo "" . $user["username"] . " | "; 44 | echo "" . $user["email"] . " | "; 45 | echo "" . $user["phone"] . " | "; 46 | echo "" . $user["group_name"] . " | "; 47 | } 48 | echo ""; 49 | } 50 | 51 | ?> 52 | 53 |
---|