├── .gitignore ├── LICENSE ├── README.md ├── config.php ├── email.php ├── functions.php ├── groupfolders.php ├── groupfolders_detail.php ├── groups.php ├── groups_detail.php ├── index.php ├── l10n ├── de.php ├── en.php └── language_iso_codes.txt ├── navigation.php ├── statistics.php ├── style.php ├── users.php └── users_detail.php /.gitignore: -------------------------------------------------------------------------------- 1 | TODO 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Nextcloud userexport 2 | PHP script to export users, groups and groupfolders using Nextcloud's OCS APIs `user metadata`, `capabilities` and `groupfolders` via cURL. 3 | 4 | ## How it works 5 | The script uses cURL to make calls to Nextcloud's OCS APIs and displays the results either through an HTML table or a CSV list that can be easily copied to calc/excel. You can download a CSV formatted file as well. 6 | 7 | https://docs.nextcloud.com/server/latest/developer_manual/client_apis/OCS/ocs-api-overview.html#user-metadata 8 | https://docs.nextcloud.com/server/latest/developer_manual/client_apis/OCS/ocs-api-overview.html#capabilities-api 9 | https://github.com/nextcloud/groupfolders#api 10 | 11 | ## Installation 12 | - Upload all files to a directory on your webserver and open `index.php` in a browser. You can point a subdomain like `https://export.cloud.example.com` at it. 13 | - **Make sure it is only accessible via https://** as you will be providing Nextcloud admin credentials to it. 14 | 15 | ## Update 16 | - If you overwrite an existing installation, remove the old files from the folder before you upload. Else you might be left with unnecessary files (although it's not an issue). 17 | Remember to backup `config.php` if you have set non-default values. 18 | 19 | ## General usage 20 | - Enter the URL of the Nextcloud target instance (https:// will be prepended automatically, if no protocol is specified) 21 | - Enter a username (userID) that has admin (or group admin) rights 22 | - Enter the corresponding password 23 | - Click on "connect" and wait (there is no progress indicator yet) 24 | 25 | After the script has successfully downloaded user, group (and groupfolder) data you can access other options from the top navigation bar. 26 | 27 | ## Security 28 | - Do not use `http://` unless you have a very good reason to do so. 29 | The script will block outgoing plain HTTP connections and warn you unless you override this security measure with `!http://...` 30 | 31 | - Group admins (as long as they do not belong themselves to the `admin` group) will not receive information on users that are members of `admin` group, even if they are members of the group the group admin manages. This is a restriction of Nextcloud itself. 32 | 33 | ## Performance 34 | API calls via cURL are slow. **Querying several hundred user accounts can take some minutes**. Be patient :) 35 | 36 | CURL parallel requests have been implemented since v0.2.0 and provide a relevant speed boost, but that's about as fast as it gets. 37 | Approximately 10-15s/100users. 38 | 39 | A progress indicator is on the wish list. 40 | 41 | ## Menu items in top nav bar 42 | 43 | ### `Users` 44 | - Choose which user metadata should be displayed or downloaded by selecting checkboxes 45 | - Change display type (if necessary) and click on display **OR** 46 | - Change the column headers option (if necessary) and download data as CSV file by clicking the download button 47 | 48 | ### `Groups` 49 | - Change display type (if necessary) and click on display **OR** 50 | - Change the column headers option (if necessary) and download data as CSV file by clicking the download button 51 | 52 | ### `Groupfolders` 53 | (only visible if groupfolders app is active and at least one groupfolder is in use) 54 | - Change display type (if necessary) and click on display **OR** 55 | - Change the column headers option (if necessary) and download data as CSV file by clicking the download button 56 | 57 | All tables can be sorted by clicking on the column headers (although not by size). 58 | 59 | ### `Email` 60 | - Select send mode ('bcc', 'cc' or 'to') 61 | - Select all users or a specific group from the dropdown list 62 | - Set filters (can be combined) 63 | - last login between two dates (including the selected days) 64 | - quota usage over a certain amount of Gigabytes (selectable in 0.5 GB steps) 65 | 66 | Clicking 'create list' will open your email application with a 'mailto:' string containing all (filtered) email addresses. 67 | 68 | ### `Statistics` 69 | - Simple overview of user/group/(groupfolder) count and quotas. 70 | 71 | ### `Logout` 72 | - This will unset (clear) php session data 73 | 74 | ## Parameters 75 | You can use the following GET parameters with this script: 76 | 77 | **Nextcloud target instance and user credentials** 78 | - `url` URL incl. protocol 79 | - `user` admin username to query the records 80 | - `pass` user password - not recommended 81 | 82 | **Display type** (display of the results page) 83 | - `type` 84 | - `table` display html formatted table [default] 85 | - `csv` display comma separated values 86 | 87 | **Message Mode** (how to send mass email) 88 | - `msg_mode` 89 | - `bcc` recommended for privacy and legal reasons [default] 90 | - `to` 91 | - `cc` 92 | 93 | **Select data to export** 94 | - `select` 95 | - `id`, `displayname`, `email`, `lastLogin`[case sensitive!], `backend`, `enabled`, `total`, `used`, `free`, `groups`, `subadmin`, `language`, `locale` 96 | 97 | **Examples:** 98 | ``` 99 | https://mydomain.org/userexport.php/?url=https://cloud.example.com&user=myusername&pass=goodpassword&type=csv 100 | https://userexport.mydomain.org/?url=https://cloud.example.com&user=myusername&msg_mode=to&select=id,displayname,enabled,used,lastLogin 101 | ``` 102 | 103 | If you do not supply one of the parameters you can fill in the corresponding fields afterwards in the form (e.g. password). 104 | Prefilled form fields can also be edited by user input. 105 | 106 | ## Configuration 107 | Several options can be set in config.php. The options are described in comments. 108 | - Authentication 109 | - Folders 110 | - UI language 111 | - UI colors 112 | 113 | ## Localization 114 | This software can be translated by adding a language file to the `l10n` directory. To translate, duplicate an existing {ISO lang code}.php file, preferably en.php, rename it to the new language code and start translating by editing the lines inside. 115 | An overview of ISO language codes can be found in the `l10n` directory, too. 116 | 117 | At present, included languages are English (default) and German. Set the language option inside config.php to select a language. 118 | 119 | ## Nextcloud integration 120 | You can integrate it by using the external sites app. 121 | - Show it only to your admin group 122 | - Set UI colors to match your theming 123 | - Prefill URL and username by using GET parameters and a nextcloud placeholder 124 | `https://export.cloud.mydomain.com/?url=https://cloud.example.com&user={uid}` 125 | 126 | ## Known Issues 127 | Sorting tables by sizes (quota) is not possible, yet. (The sort function cannot handle human readable formats like `50 GB`) 128 | 129 | ## Development 130 | Any hints to enhancements or security issues are highly welcome. 131 | If you would like to contribute, please open an issue or a pull request. 132 | 133 | Minor version updates and bugfixes (x.x.1) are not always released separately. If you want to use the latest version please download/clone from master. 134 | 135 | You can still use the simpler v0.4.1, if you prefer. 136 | 137 | ## Additional Info 138 | Inspired by comments to this github issue: 139 | https://github.com/nextcloud/server/issues/14715 140 | 141 | ## Screenshots (still v1.0.0, TODO) 142 | **Login page** 143 | ![nextcloud-userexport_v1 0 0_login_page](https://user-images.githubusercontent.com/29312856/75972989-bcb0c300-5ed4-11ea-9024-401e0f13d87c.png) 144 | 145 | **Data selection** 146 | ![nextcloud-userexport_v1 0 0_users_page](https://user-images.githubusercontent.com/29312856/75974056-7b211780-5ed6-11ea-890f-ca5c35a82631.png) 147 | 148 | **Userlist** 149 | ![nextcloud-userexport_v1 0 0_users_details_page](https://user-images.githubusercontent.com/29312856/75973031-ce926600-5ed4-11ea-9f12-360c9dfdc10a.png) 150 | -------------------------------------------------------------------------------- /config.php: -------------------------------------------------------------------------------- 1 | '.L10N_CONNECTION_NEEDED); 14 | } 15 | 16 | if($_POST['submit']) { 17 | 18 | $_SESSION['message_mode'] = $_POST['message_mode'] ?? $_SESSION['message_mode']; 19 | $_SESSION['filters_set'] = array_keys($_POST, 'set_filter'); 20 | $_SESSION['filter_group'] = $_POST['filter_group']; 21 | $_SESSION['filter_quota'] = $_POST['filter_quota']; 22 | $_SESSION['type_quota'] = $_POST['type_quota'] ?? null; 23 | $_SESSION['compare_quota'] = $_POST['compare_quota'] ?? null; 24 | 25 | $_SESSION['filter_ll_since'] = $_POST['filter_ll_since'] != "" 26 | ? $_POST['filter_ll_since'] 27 | : '1970-01-01'; 28 | 29 | $_SESSION['filter_ll_before'] = $_POST['filter_ll_before'] != "" 30 | ? $_POST['filter_ll_before'] 31 | : date('Y-m-d'); 32 | 33 | $userlist = $_SESSION['filters_set'] 34 | ? filter_users() 35 | : $_SESSION['userlist']; 36 | 37 | header("Location: ".build_mailto_list($_SESSION['message_mode'], $userlist)); 38 | 39 | } 40 | 41 | ?> 42 | 43 | 44 | 45 | 46 | Nextcloud Userexport 47 | 48 | 49 | 50 | "; 57 | 58 | print_status_overview(); 59 | 60 | echo "
61 |
62 | 63 | 64 | 76 | 77 | 78 | 92 | 93 | 94 | 113 | 114 | 115 | 116 | 134 | 135 |
".L10N_SEND_AS.""; 65 | 66 | $value = ['bcc','cc','to']; 67 | 68 | foreach($value as $mode) { 69 | echo " "; 73 | } 74 | 75 | echo "
".L10N_SEND_TO." 79 | 80 | 81 | 83 | 84 |
".L10N_FILTER_BY." 95 | 97 | 98 | ".L10N_AND." 104 | 112 |
117 | 119 | 120 | 125 | 131 | GB 133 |
"; 136 | 137 | echo "
139 |
"; 140 | 141 | ?> 142 | 143 | 144 | -------------------------------------------------------------------------------- /functions.php: -------------------------------------------------------------------------------- 1 | L10N_USER_ID, 'displayname' => L10N_DISPLAYNAME, 51 | 'email' => L10N_EMAIL, 'lastLogin' => L10N_LAST_LOGIN, 52 | 'backend' => L10N_BACKEND, 'enabled' => L10N_ENABLED, 53 | 'quota' => L10N_QUOTA, 'used' => L10N_QUOTA_USED, 54 | 'percentage_used' => L10N_PERCENTAGE_USED, 'free' => L10N_QUOTA_FREE, 55 | 'groups' => L10N_GROUPS, 'subadmin' => L10N_SUBADMIN, 56 | 'language' => L10N_LANGUAGE, 'locale' => L10N_LOCALE]; 57 | } 58 | 59 | /** 60 | * Check secure outgoing connection 61 | * 62 | * Depending on the first five chars of the supplied URL: 63 | * - In case 'https' -> return unchanged URL 64 | * - In case '!http' -> remove '!' and return trimmed URL 65 | * - In case 'http:' -> exit with insecure connection warning 66 | * - In case 'none of the above' -> prepend input with https:// and return it 67 | * 68 | * @param $input_url URL to be processed 69 | * 70 | * @return $output_url URL after processing 71 | * 72 | */ 73 | function check_https($input_url) { 74 | 75 | require 'config.php'; 76 | 77 | // Prepare error message, depending on https_strict config option 78 | $error_msg = " 79 |
80 | ".L10N_HTTP_IS_BLOCKED." 81 |
".L10N_HTTPS_RECOMMENDATION." 82 |
"; 83 | 84 | if (!$https_strict) 85 | $error_msg .= "
".L10N_HTTPS_OVERRIDE_HINT." 86 |
".L10N_EG."!http://cloud.example.com
"; 87 | else 88 | $error_msg .= "
".L10N_HTTPS_STRICT_MODE."
"; 89 | 90 | // Save the first five chars of the URL to a new variable '$trim_url' 91 | $trimmed_url = substr($input_url,0,5); 92 | 93 | switch($trimmed_url) { 94 | 95 | // Leave URL untouched if https:// protocol is already set 96 | case 'https': 97 | $output_url = $input_url; 98 | break; 99 | 100 | // Check if plain HTTP is used without override command and exit if not 101 | case 'http:': 102 | header('Content-Type: text/html; charset=utf-8'); 103 | exit($error_msg); 104 | break; 105 | 106 | // Remove '!' if HTTPS check override is selected by use of '!http' 107 | case '!http': 108 | 109 | if($https_strict) { 110 | header('Content-Type: text/html; charset=utf-8'); 111 | exit($error_msg); 112 | } 113 | 114 | $output_url = ltrim($input_url,'!'); 115 | break; 116 | 117 | default: 118 | $output_url = "https://".$input_url; 119 | break; 120 | } 121 | return $output_url; 122 | } 123 | 124 | /** 125 | * Remove httpx:// from given URL and return the trimmed version 126 | * 127 | * @param $url URL to be trimmed 128 | * 129 | * @return $trim_url URL without http:// or https:// 130 | * 131 | */ 132 | function removehttpx($input_url) { 133 | $trimmed_url = preg_replace('(^https?://)', '', $input_url); 134 | return $trimmed_url; 135 | } 136 | 137 | /** 138 | * Error handling for cURL requests 139 | * 140 | * Checks cURL error codes and statuscodes in the API response 141 | * 142 | * @param $ch cURL handle to be checked 143 | * @param $data variable containing the API data fetched by cURL exec 144 | * 145 | */ 146 | function check_curl_response($ch, $data) { 147 | 148 | // check if cURL returns an error != 0 149 | if (curl_errno($ch)) { 150 | // Iterate through common cURL error codes, exit and return custom error messages or default message 151 | switch (curl_errno($ch)) { 152 | case 6: 153 | header('Content-Type: text/html; charset=utf-8'); 154 | exit(' 155 | 156 |
157 | '.L10N_ERROR.'cURL ('.L10N_STATUSCODE.curl_errno($ch).') 158 |
'.curl_error($ch). 159 | ' 160 |
'.L10N_ERROR_CURL_CONNECTION.' 161 |
'); 162 | case 51: 163 | header('Content-Type: text/html; charset=utf-8'); 164 | exit(' 165 | 166 |
167 | '.L10N_ERROR.'cURL ('.L10N_STATUSCODE.curl_errno($ch).') 168 |
'.curl_error($ch). 169 | ' 170 |
171 | '.L10N_ERROR_URL.' 172 |
'); 173 | default: 174 | header('Content-Type: text/html; charset=utf-8'); 175 | exit(' 176 | 177 |
178 | '.L10N_ERROR.'cURL ('.L10N_STATUSCODE.curl_errno($ch).') 179 |
'.curl_error($ch).' 180 |
181 |
'); 182 | } 183 | } 184 | 185 | if ($data === null) { 186 | header('Content-Type: text/html; charset=utf-8'); 187 | exit(' 188 | 189 |
190 | '.L10N_ERROR . L10N_ERROR_EMPTY_API_RESPONSE.' 191 |
192 |
'); 193 | } 194 | // Read statuscode from API response 195 | $status = $data['ocs']['meta']['statuscode']; 196 | 197 | // Iterate through possible statuscode responses 198 | switch ($status) { 199 | case 100: 200 | case 200: 201 | // 100 or 200 mean OK -> break switch case and continue normal script execution 202 | break; 203 | case 404: 204 | header('Content-Type: text/html; charset=utf-8'); 205 | exit(' 206 | 207 |
208 | '.L10N_ERROR . L10N_USER_DOES_NOT_EXIST 209 | .' ('.L10N_STATUSCODE.$status.') 210 |
211 |
'); 212 | break; 213 | case 997: 214 | header('Content-Type: text/html; charset=utf-8'); 215 | exit(' 216 | 217 |
'.L10N_ERROR . L10N_AUTHENTICATION.' ('.L10N_STATUSCODE.$status.') 218 |
'.L10N_CHECK_USER_PASS.' 219 | 220 |
'.L10N_HINT_ADMIN_OR_GROUP_ADMIN.' 221 |
'); 222 | default: 223 | header('Content-Type: text/html; charset=utf-8'); 224 | exit(' 225 | 226 |
227 | '.L10N_ERROR . L10N_UNKNOWN.' ('.L10N_STATUSCODE.$status.') 228 |
229 |
'); 230 | } 231 | } 232 | 233 | /** 234 | * Fetch the list containing all user IDs from the server 235 | * 236 | */ 237 | function fetch_userlist() { 238 | 239 | // Log start timestamp 240 | $timestamp_start = microtime(true); 241 | 242 | // Initialize cURL handle to fetch user ID list and set options 243 | $ch = curl_init(); 244 | set_curl_options($ch, 'users'); 245 | 246 | // Fetch raw userlist and store user_ids in $users 247 | $users_raw = json_decode(curl_exec($ch), true); 248 | 249 | // Check for errors in cURL request 250 | check_curl_response($ch, $users_raw); 251 | 252 | // Drop cURL handle 253 | curl_close($ch); 254 | 255 | // Check if the userlist has been received and save user IDs to $users 256 | if (isset($users_raw['ocs']['data']['users'])) { 257 | $users = $users_raw['ocs']['data']['users']; 258 | // Set the session variable 'authenticated' to true to access other pages 259 | $_SESSION['authenticated'] = true; 260 | } 261 | 262 | $_SESSION['userlist'] = $users; 263 | 264 | // Calculate time for function execution and save as $_SESSION variable 265 | $_SESSION['time_fetch_userlist'] = round(microtime(true) - $timestamp_start,1); 266 | 267 | } 268 | 269 | /** 270 | * Fetch the list containing all group IDs from the server 271 | * 272 | * @return $groups 273 | * 274 | */ 275 | function fetch_grouplist() { 276 | 277 | // Log start timestamp 278 | $timestamp_start = microtime(true); 279 | 280 | // Initialize cURL handle to fetch user id list and set options 281 | $ch = curl_init(); 282 | set_curl_options($ch, 'groups'); 283 | 284 | // Fetch raw userlist and store user_ids in $users 285 | $groups_raw = json_decode(curl_exec($ch), true); 286 | 287 | // Check for errors in cURL request 288 | check_curl_response($ch, $groups_raw); 289 | 290 | // Drop cURL handle 291 | curl_close($ch); 292 | 293 | // Check if the userlist has been received and save user IDs to $users 294 | $groups = $groups_raw['ocs']['data']['groups'] ?? null; 295 | 296 | $_SESSION['grouplist'] = $groups; 297 | 298 | // Calculate time for function execution and save as $_SESSION variable 299 | $_SESSION['time_fetch_grouplist'] = round(microtime(true) - $timestamp_start,1); 300 | 301 | } 302 | 303 | /** 304 | * Initialize individual cURL handles, set options and append them to multi handle list 305 | * 306 | * @return $raw_user_data 307 | * 308 | */ 309 | function fetch_raw_user_data() { 310 | 311 | // Log start timestamp 312 | $timestamp_start = microtime(true); 313 | 314 | // Initialize cURL multi handle for parallel requests 315 | $mh = curl_multi_init(); 316 | 317 | // Iterate through userlist 318 | foreach($_SESSION['userlist'] as $key => $user_id) { 319 | // Initialize cURL handle 320 | $curl_requests[$key] = curl_init(); 321 | // Set cURL options for this handle 322 | set_curl_options($curl_requests[$key], 'users', $user_id); 323 | // Add created handle to multi handle list 324 | curl_multi_add_handle($mh, $curl_requests[$key]); 325 | } 326 | 327 | /** 328 | * Fetch user data via cURL using parallel connections (curl_multi_*) 329 | */ 330 | do { 331 | $status = curl_multi_exec($mh, $active); 332 | if ($active) { 333 | curl_multi_select($mh); 334 | } 335 | } while ($active && $status == CURLM_OK); 336 | 337 | /** 338 | * Save content to $selected_user_data 339 | */ 340 | //Iterate through $curl_requests (the cURL handle list) 341 | foreach ($curl_requests as $key => $request) { 342 | // Get content of one user data request, store in $single_user_data 343 | $raw_user_data[] = 344 | json_decode(curl_multi_getcontent($curl_requests[$key]),true); 345 | 346 | // Remove processed cURL handle 347 | curl_multi_remove_handle($mh, $curl_requests[$key]); 348 | } 349 | // Drop cURL multi handle 350 | curl_multi_close($mh); 351 | 352 | // Calculate time for function execution and save as $_SESSION variable 353 | $_SESSION['time_fetch_userdata'] = round(microtime(true) - $timestamp_start,1); 354 | 355 | // Calculate total script runtime 356 | $_SESSION['time_total'] = number_format(round( 357 | microtime(true) - $_SESSION['timestamp_script_start'], 1),1); 358 | 359 | // Save timestamp when data transfer was finished to $_SESSION variable 360 | $_SESSION['timestamp_data'] = date(DATE_ATOM); 361 | 362 | return $raw_user_data; 363 | } 364 | 365 | /** 366 | * Download groupfolder data if enabled on the server 367 | * 368 | */ 369 | function fetch_raw_groupfolders_data() { 370 | 371 | // Log start timestamp 372 | $timestamp_start = microtime(true); 373 | 374 | // Initialize cURL handle to fetch user id list and set options 375 | $ch = curl_init(); 376 | set_curl_options($ch, 'groupfolders'); 377 | 378 | // Fetch raw userlist and store user_ids in $users 379 | $_SESSION['raw_groupfolders_data'] = json_decode(curl_exec($ch), true); 380 | 381 | // Set session variable telling if groupfolders is active on connected server 382 | $_SESSION['groupfolders_active'] = isset($_SESSION['raw_groupfolders_data']); 383 | 384 | // Drop cURL handle 385 | curl_close($ch); 386 | 387 | // Calculate time for function execution and save as $_SESSION variable 388 | $_SESSION['time_fetch_groupfolders'] = round(microtime(true) - $timestamp_start,1); 389 | 390 | } 391 | 392 | /** 393 | * Download groupfolder data 394 | * 395 | */ 396 | function fetch_server_capabilities() { 397 | // Initialize cURL handle to fetch user id list and set options 398 | $ch = curl_init(); 399 | set_curl_options($ch, 'capabilities'); 400 | 401 | // Fetch raw userlist and store user_ids in $users 402 | $_SESSION['raw_server_capabilities'] = json_decode(curl_exec($ch), true); 403 | 404 | // Check for errors in cURL request 405 | check_curl_response($ch, $_SESSION['raw_server_capabilities']); 406 | 407 | // Drop cURL handle 408 | curl_close($ch); 409 | } 410 | 411 | /** 412 | * Iterate through userlist and call select_data_single_user each time 413 | * 414 | * @param $data_choices Array containing a list of data columns to be taken into account 415 | * OPTIONAL DEFAULT: null 416 | * @param $format decode UTF8? 417 | * OPTIONAL DEFAULT: null (decode UTF8) 418 | * 419 | * @return $selected_user_data 420 | * ARRAY 421 | */ 422 | function select_data_all_users($data_choices = null, $userlist = null, $format = null, 423 | $csv_delimiter = ', ') { 424 | 425 | $data_choices = $data_choices ?? $_SESSION['data_choices']; 426 | $userlist = $userlist ?? $_SESSION['userlist']; 427 | 428 | foreach($userlist as $key => $user_id) { 429 | // Call select_data function to filter/format request data 430 | $selected_user_data[] = select_data_single_user( 431 | $_SESSION['raw_user_data'][$key], $user_id, $data_choices, $format, 432 | $csv_delimiter); 433 | } 434 | 435 | return $selected_user_data; 436 | } 437 | 438 | /** 439 | * Select elements from array "$data" and format for csv download 440 | * or browser display depending on parameters 441 | * 442 | * @param $data Single user record data array 443 | * @param $user_id ID of the user to be processed 444 | * @param $data_choices Array containing a list of data columns to be taken into account 445 | * @param $format If not 'csv', data will be prepared for browser display 446 | * OPTIONAL DEFAULT: null 447 | * 448 | * @return $selected_data Result of $data filtering 449 | * ARRAY 450 | */ 451 | function select_data_single_user( 452 | $data, $user_id, $data_choices, $format = null, $csv_delimiter = ', ') { 453 | 454 | // If data is not returned due to missing permissions (group admins) set 'N/A' instead 455 | if($data['ocs']['meta']['statuscode'] == 997) { 456 | $selected_data[] = $user_id; 457 | for($i = 1; $i < count($data_choices); $i++) 458 | $selected_data[] = 'N/A'; 459 | } 460 | 461 | // Prepare data for CSV file export if $format = 'csv' 462 | else { 463 | // Iterate through chosen data sets 464 | foreach($data_choices as $key => $item) { 465 | $quota = $data['ocs']['data']['quota']['quota']; 466 | $used = $data['ocs']['data']['quota']['used']; 467 | $backend = $data['ocs']['data']['backend']; 468 | 469 | $item_data = $item !== 'percentage_used' 470 | ? $data['ocs']['data'][$item] 471 | : ((in_array($quota, [-3, 0, 'none']) || $backend === 'Guests') 472 | ? 'N/A' 473 | : round($used / $quota * 100)); 474 | 475 | // Filter/format different data sets 476 | switch($item) { 477 | 478 | // Convert email data set to lowercase 479 | case 'email': 480 | $selected_data[] = $item_data == null ? '-' : strtolower($item_data); 481 | break; 482 | 483 | case 'lastLogin': 484 | // If user has never logged in set $last_login to '-' 485 | $selected_data[] = $item_data == 0 486 | ? ($format == 'csv' 487 | ? '-' 488 | : '') 489 | // Format unix timestamp to YYYY-MM-DD after trimming last 3 chars 490 | : date("Y-m-d", substr($item_data, 0, 10)); 491 | break; 492 | 493 | // Make the display of 'enabled' bool pretty in the browser 494 | case 'enabled': 495 | $selected_data[] = $format == 'csv' 496 | ? $item_data 497 | : ($item_data == true 498 | ? '' 499 | : ''); 500 | break; 501 | 502 | case 'quota': 503 | case 'free': 504 | if($backend === 'Guests') { 505 | $selected_data[] = 'N/A'; 506 | break; 507 | } 508 | $item_data = $data['ocs']['data']['quota'][$item]; 509 | $selected_data[] = in_array($item_data, [-3, 'none'], true) 510 | ? '∞' 511 | : ($format != 'csv' 512 | ? format_size($item_data, 'no_filter') 513 | : $item_data); 514 | break; 515 | 516 | case 'used': 517 | if($backend === 'Guests') { 518 | $selected_data[] = 'N/A'; 519 | break; 520 | } 521 | $item_data = $data['ocs']['data']['quota'][$item]; 522 | $selected_data[] = $format != 'csv' 523 | ? format_size($item_data) 524 | : $item_data; 525 | break; 526 | 527 | // Convert arrays 'subadmin' and 'groups' to comma separated values and wrap them in parentheses if not null 528 | case 'subadmin': 529 | case 'groups': 530 | $selected_data[] = empty($item_data) 531 | ? '-' 532 | : ($format != 'csv' 533 | ? build_csv_line($item_data, false, $csv_delimiter) 534 | : build_csv_line($item_data)); 535 | break; 536 | 537 | case 'locale': 538 | // If user has not set a locale use '-' 539 | $selected_data[] = $item_data == '' ? '-' : $item_data; 540 | break; 541 | 542 | // If none of the above apply 543 | default: 544 | $selected_data[] = $item_data; 545 | } 546 | } 547 | } 548 | return $selected_data; 549 | } 550 | 551 | /** 552 | * TODO 553 | */ 554 | function select_data_all_users_filter($filter_by, $conditions, 555 | $filter_option = null) { 556 | 557 | foreach($_SESSION['userlist'] as $key => $user_id) { 558 | 559 | if($filter_by == 'quota' || $filter_by == 'used' || $filter_by == 'free') { 560 | $item_data = $_SESSION['raw_user_data'][$key]['ocs']['data']['quota'][$filter_by]; 561 | $limit_to_check = $conditions * 1073741824; // Gibibytes not Gigabytes (1024³) 562 | } 563 | else 564 | $item_data = $_SESSION['raw_user_data'][$key]['ocs']['data'][$filter_by]; 565 | 566 | switch($filter_by) { 567 | 568 | case 'quota': 569 | case 'used': 570 | case 'free': 571 | require 'config.php'; 572 | switch ($filter_option) { 573 | case 'gt': 574 | if($item_data > $limit_to_check) 575 | $selected_user_ids[] = $user_id; 576 | break; 577 | case 'lt': 578 | if($item_data < $limit_to_check) 579 | $selected_user_ids[] = $user_id; 580 | break; 581 | case 'asymp': 582 | if($item_data > $limit_to_check * (1 - $filter_tolerance) 583 | && $item_data < $limit_to_check * (1 + $filter_tolerance)) 584 | $selected_user_ids[] = $user_id; 585 | break; 586 | case 'equals': 587 | if($item_data == $limit_to_check) 588 | $selected_user_ids[] = $user_id; 589 | break; 590 | } 591 | break; 592 | 593 | case 'lastLogin': 594 | $lastLogin = substr($item_data, 0, 10); 595 | 596 | if($lastLogin >= strtotime($conditions[0]) 597 | && $lastLogin <= strtotime($conditions[1].' +1 day')) 598 | $selected_user_ids[] = $user_id; 599 | break; 600 | 601 | case 'groups': 602 | case 'subadmin': 603 | if(in_array($conditions, $item_data)) 604 | $selected_user_ids[] = $user_id; 605 | break; 606 | 607 | } 608 | } 609 | 610 | if(!$selected_user_ids) 611 | $selected_user_ids = ['']; 612 | 613 | return $selected_user_ids; 614 | } 615 | 616 | function filter_users() { 617 | 618 | if($_SESSION['filters_set']) { 619 | 620 | $filter_conditions_ll = [$_SESSION['filter_ll_since'], $_SESSION['filter_ll_before']]; 621 | 622 | $uids_g = in_array('filter_group_choice', $_SESSION['filters_set']) 623 | ? select_data_all_users_filter('groups', $_SESSION['filter_group']) 624 | : $_SESSION['userlist']; 625 | 626 | $uids_l = in_array('filter_lastLogin_choice', $_SESSION['filters_set']) 627 | ? select_data_all_users_filter('lastLogin', $filter_conditions_ll) 628 | : $_SESSION['userlist']; 629 | 630 | $uids_q = in_array('filter_quota_choice', $_SESSION['filters_set']) 631 | ? select_data_all_users_filter($_SESSION['type_quota'], 632 | $_SESSION['filter_quota'], $_SESSION['compare_quota']) 633 | : $_SESSION['userlist']; 634 | 635 | $user_ids = array_intersect($_SESSION['userlist'], $uids_g, $uids_l, $uids_q); 636 | 637 | } 638 | 639 | if(!$user_ids) 640 | exit('No users found matching filter settings'); 641 | 642 | return $user_ids; 643 | 644 | } 645 | 646 | /** 647 | * Find all users belonging to a given group an return an array containing userID and displayname 648 | * 649 | * @param $group The name of the group to search for 650 | * @param $format If not 'csv', data will be prepared for browser display 651 | * OPTIONAL DEFAULT: null 652 | * 653 | * @return $group_members 654 | * 655 | */ 656 | function select_group_members($group, $format = null) { 657 | // Iterate through userlist 658 | foreach($_SESSION['userlist'] as $key => $user_id) { 659 | // Call select_data function to filter/format request data 660 | $data = $_SESSION['raw_user_data'][$key]; 661 | if(in_array($group, $data['ocs']['data']['groups'])) 662 | $group_members[] = [$user_id, $data['ocs']['data']['displayname']]; 663 | } 664 | return $group_members; 665 | } 666 | 667 | /** 668 | * Calculate how much disk space is assigned, used and available in total 669 | * (user and groupfolder quota) 670 | * 671 | */ 672 | function calculate_quota() { 673 | 674 | // Reset values 675 | $_SESSION['quota_total_assigned'] = 0; 676 | $_SESSION['quota_total_free'] = 0; 677 | $_SESSION['quota_total_used'] = 0; 678 | 679 | // Loop through raw user data and add quota item values to $_SESSION variables 680 | foreach($_SESSION['raw_user_data'] as $user_data) { 681 | 682 | $_SESSION['quota_total_used'] += $user_data['ocs']['data']['quota']['used']; 683 | $_SESSION['quota_total_free'] += 684 | $user_data['ocs']['data']['quota']['free']; 685 | 686 | $quota_assigned = $user_data['ocs']['data']['quota']['quota']; 687 | 688 | $_SESSION['quota_total_assigned'] += $quota_assigned > 0 689 | ? $quota_assigned 690 | : 0; 691 | 692 | $_SESSION['quota_total_assigned_infin'] = ($quota_assigned == -3); 693 | 694 | } 695 | 696 | if($_SESSION['groupfolders_active']) 697 | 698 | // Reset values 699 | $_SESSION['quota_groupfolders_used'] = 0; 700 | $_SESSION['quota_groupfolders_assigned'] = 0; 701 | 702 | foreach($_SESSION['raw_groupfolders_data']['ocs']['data'] as $groupfolder) { 703 | $_SESSION['quota_groupfolders_used'] += $groupfolder['size']; 704 | $_SESSION['quota_groupfolders_assigned'] += $groupfolder['quota']; 705 | } 706 | 707 | } 708 | 709 | /** 710 | * Print status message on successful server connection 711 | * 712 | * Status message contains user count, target instance, timestamp and runtime in seconds 713 | * 714 | * Change the standard in 'date(DATE_ATOM)' used to display the timestamp to your needs 715 | * 716 | */ 717 | function print_status_success() { 718 | // Output status message after receiving user and group data 719 | echo ' 720 |
'.L10N_CONNECTED_TO_SERVER.removehttpx($_SESSION['target_url']) 721 | .' ' 722 | .'
'.L10N_DOWNLOADED.' '.count($_SESSION['raw_user_data']).' ' 723 | .L10N_USERS_AND.' '.count($_SESSION['grouplist']).' ' 724 | .L10N_GROUPS_IN.' '.$_SESSION['time_total'].' '.L10N_SECONDS 725 | .'
Timestamp: '.$_SESSION['timestamp_data']. 726 | '
' 727 | .L10N_ACCESS_TO_ALL_MENU_OPTIONS.''; 728 | } 729 | 730 | /** 731 | * Status printed on each page, showing the active server and user/group count 732 | * 733 | */ 734 | function print_status_overview($scope = "quick") { 735 | 736 | $infinite = $_SESSION['quota_total_assigned_infin'] 737 | ? " (+ ∞)" 738 | : ""; 739 | 740 | if($scope == "quick") { 741 | echo "
742 | " 743 | .removehttpx($_SESSION['target_url'])." 744 |
"; 745 | 746 | } else { 747 | 748 | fetch_server_capabilities(); 749 | 750 | $_SESSION['server_version_string'] = 751 | $_SESSION['raw_server_capabilities']['ocs']['data']['version']['string']; 752 | 753 | echo "
754 | " 755 | .removehttpx($_SESSION['target_url'])." 756 | (".L10N_NEXTCLOUD." v{$_SESSION['server_version_string']}) 757 |
758 | 759 | 760 | 761 | 762 | 763 | 764 | 765 | 766 | 767 | 768 | 769 | "; 770 | 771 | if($_SESSION['groupfolders_active']) 772 | echo " 773 | 774 | 775 | "; 776 | 777 | echo " 778 |
Overall count
".L10N_USERS."{$_SESSION['user_count']}
".L10N_GROUPS."{$_SESSION['group_count']}
".L10N_GROUPFOLDERS."{$_SESSION['groupfolders_count']}
779 |
780 | 781 | 782 | 783 | 784 | 785 | 786 | 787 | 788 | 789 | 790 | 791 | 792 | 793 | 794 | 795 | 796 | "; 797 | 798 | if($_SESSION['groupfolders_active']) 799 | echo " 800 | 801 | 802 | 803 | 804 | 805 | 806 | 807 | 808 | 809 | 810 | 811 | 812 | 813 |
".L10N_USERS."
".L10N_QUOTA_USED."".format_size($_SESSION['quota_total_used'])."
".L10N_QUOTA."".format_size($_SESSION['quota_total_assigned'])."$infinite
".L10N_QUOTA_FREE."".format_size($_SESSION['quota_total_free'])."
".L10N_GROUPFOLDERS."
".L10N_QUOTA_USED."".format_size($_SESSION['quota_groupfolders_used'])."
".L10N_QUOTA."".format_size($_SESSION['quota_groupfolders_assigned'])."
"; 814 | 815 | echo "
816 | 817 | 818 | 819 | 820 | 821 | 822 | 823 | 824 | 825 | 826 | 827 | 828 | 829 | 830 | 831 | 832 | 833 | 834 | 835 |
".L10N_EXECUTION_TIMES."
".L10N_FETCH_USERLIST."{$_SESSION['time_fetch_userlist']} s
".L10N_FETCH_GROUPLIST."{$_SESSION['time_fetch_grouplist']} s
".L10N_FETCH_GROUPFOLDERS."{$_SESSION['time_fetch_groupfolders']} s
".L10N_FETCH_USERDATA."{$_SESSION['time_fetch_userdata']} s

" 836 | .L10N_DATA_RETRIEVED." {$_SESSION['timestamp_data']}"; 837 | } 838 | } 839 | 840 | /** 841 | * Build CSV file 842 | * 843 | * Creates a file containing provided array data as comma separated values 844 | * 845 | * @param $list Data array containing user data 846 | * @param $headers CSV line containing the column headers, set null if none 847 | * OPTIONAL DEFAULT: List from $data_choices variable 848 | * 849 | * @return $csv_filename Filename of the newly created file 850 | * 851 | */ 852 | function build_csv_file($list, $headers = 'default') { 853 | 854 | if(!$_SESSION['temp_folder']) 855 | $_SESSION['temp_folder'] = 'export_temp-'.bin2hex(random_bytes(16)); 856 | 857 | // Delete temporary folder and contents 858 | delete_temp_folder(); 859 | 860 | // Create headers from session variable 'data_choices' if not supplied 861 | if($headers == 'default') 862 | $headers = build_csv_line(); 863 | 864 | // Set random filename 865 | $csv_filename = bin2hex(random_bytes(8)).'.csv'; 866 | 867 | // Check if temporary folder already exists, else make directory 868 | if(!file_exists($_SESSION['temp_folder'])) 869 | mkdir($_SESSION['temp_folder'], 0755, true); 870 | 871 | // Create/open file with write access and return file handle 872 | $csv_file = fopen($_SESSION['temp_folder'].'/'.$csv_filename, "w"); 873 | 874 | // Set file permissions (rw-r-----) 875 | chmod($_SESSION['temp_folder'].'/'.$csv_filename, 0640); 876 | 877 | // Write selected headers as first line to file 878 | if($headers != 'no_headers') 879 | fwrite($csv_file, $headers."\n"); 880 | 881 | // Iterate through provided data array and append each line to the file 882 | foreach($list as $line) 883 | fputcsv($csv_file, $line); 884 | 885 | // Close active file handle 886 | fclose($csv_file); 887 | return $csv_filename; 888 | } 889 | 890 | /** 891 | * Initiate file download 892 | * 893 | * The selected file (by filename) will be downloaded and deleted afterwards 894 | * It can be downloaded using an alternative filename, if supplied 895 | * 896 | * @param $filename Filename on the server 897 | * @param $mime_type MIME type to be sent in the header 898 | * OPTIONAL DEFAULT: 'text/csv' 899 | * @param $filename_download Filename for download 900 | * OPTIONAL DEFAULT: 'download' 901 | * @param $folder Folder to prepend in front of the server filename 902 | * OPTIONAL DEFAULT: '.' 903 | * 904 | */ 905 | function download_file($filename, $mime_type = 'text/csv', 906 | $filename_download = 'download', $folder = '.') { 907 | 908 | // make sure file is deleted even if user cancels download 909 | ignore_user_abort(true); 910 | 911 | header('Content-Type: '.$mime_type); 912 | header("Content-Transfer-Encoding: Binary"); 913 | header("Content-disposition: attachment; filename=\"".$filename_download."\""); 914 | 915 | readfile($folder.'/'.$filename); 916 | 917 | // delete file 918 | unlink($folder.'/'.$filename); 919 | 920 | // delete folder 921 | if($folder != "." && $folder != "..") 922 | rmdir($folder); 923 | } 924 | 925 | /** 926 | * Delete content of specified folder 927 | * 928 | * Do nothing if no foldername or the script base folder is provided 929 | * 930 | * @param $folder Foldername to delete content in 931 | * 932 | */ 933 | function delete_temp_folder() { 934 | 935 | // Get filelist from target folder 936 | $files = glob('export_temp-*/*'); 937 | 938 | // Iterate through filelist and delete all (except hidden files e.g. .htaccess) 939 | foreach($files as $file) 940 | if(is_file($file)) 941 | unlink($file); 942 | 943 | // Delete folder(s) 944 | foreach(glob('export_temp-*') as $temp_dir) 945 | rmdir($temp_dir); 946 | } 947 | 948 | /** 949 | * Build and format userlist 950 | * 951 | * Creates and returns a formatted HTML table containing the userlist 952 | * 953 | * @param $user_data Array containing selected and formatted user data 954 | * 955 | * @return $table_user_data_headers concatenated with $table_user_data 956 | * 957 | */ 958 | function build_table_user_data($user_data) { 959 | 960 | require 'config.php'; 961 | $data_choices = $_SESSION['data_choices']; 962 | 963 | if($_SESSION['filters_set']) { 964 | echo count($user_data)." ".L10N_USERS." ".L10N_FILTERED_BY. 965 | ""; 966 | 967 | $quota = $_SESSION['filter_quota']; 968 | 969 | foreach($_SESSION['filters_set'] as $filter) 970 | switch($filter) { 971 | case 'filter_group_choice': 972 | 973 | echo " 974 | 975 | 976 | "; 977 | break; 978 | 979 | case 'filter_lastLogin_choice': 980 | 981 | if($_SESSION['filter_ll_since'] == '1970-01-01') { 982 | $since = ''; 983 | $delimiter = '<= '; 984 | } 985 | else { 986 | $since = $_SESSION['filter_ll_since']; 987 | $delimiter = ' <--> '; 988 | } 989 | 990 | $before = $_SESSION['filter_ll_before'] != date('Y-m-d') 991 | ? $_SESSION['filter_ll_before'] 992 | : L10N_TODAY; 993 | 994 | echo " 995 | 996 | 997 | "; 998 | break; 999 | 1000 | case 'filter_quota_choice': 1001 | 1002 | switch($_SESSION['compare_quota']) { 1003 | 1004 | case 'gt': 1005 | $compare = '>'; 1006 | break; 1007 | case 'lt': 1008 | $compare = '<'; 1009 | break; 1010 | case 'asymp': 1011 | require 'config.php'; 1012 | $compare = 'approximately'; 1013 | $quota_limits = "(".$quota * (1 - $filter_tolerance)." - " 1014 | .$quota * (1 + $filter_tolerance)." GiB)"; 1015 | break; 1016 | case 'equals': 1017 | $compare = '='; 1018 | break; 1019 | 1020 | } 1021 | 1022 | switch($_SESSION['type_quota']) { 1023 | case 'used': 1024 | $type_quota = L10N_USED; 1025 | break; 1026 | case 'quota': 1027 | $type_quota = L10N_ASSIGNED; 1028 | break; 1029 | case 'free': 1030 | $type_quota = L10N_FREE; 1031 | break; 1032 | } 1033 | 1034 | 1035 | echo " 1036 | 1037 | 1040 | "; 1041 | break; 1042 | } 1043 | echo "
• ".L10N_GROUP.":{$_SESSION['filter_group']}
• ".L10N_LAST_LOGIN.":$since$delimiter$before
• ".L10N_DISK_SPACE." $type_quota: 1038 | $compare $quota GiB $quota_limits 1039 |

"; 1044 | } 1045 | 1046 | // Define HTML table and set header cell content 1047 | $table_user_data_headers = ""; 1048 | 1049 | foreach($data_choices as $key => $choice) { 1050 | $sort = (in_array($choice, ['quota', 'used', 'free'])) 1051 | ? null 1052 | : " onclick='sortTable()'"; 1053 | 1054 | $align = (in_array($choice, 1055 | ['quota', 'used', 'free', 'lastLogin', 'percentage_used'])) 1056 | ? "text-align: center;" 1057 | : null; 1058 | 1059 | foreach($_SESSION['data_options'] as $option => $title) { 1060 | if($choice == $option) 1061 | $choice = $title; 1062 | } 1063 | 1064 | $table_user_data_headers .= "$choice"; 1065 | } 1066 | $table_user_data_headers .= ""; 1067 | 1068 | // Search for and return position of quota keys in $data_choices 1069 | $keypos_right_align[] = array_search('quota', $data_choices); 1070 | $keypos_right_align[] = array_search('used', $data_choices); 1071 | $keypos_right_align[] = array_search('free', $data_choices); 1072 | $keypos_right_align[] = $keypos_percentage_used = 1073 | array_search('percentage_used', $data_choices); 1074 | 1075 | 1076 | // Search for and return position of 'enabled' and 'lastLogin' in $data_choices 1077 | $keypos_center_align[] = array_search('enabled', $data_choices); 1078 | $keypos_center_align[] = array_search('lastLogin', $data_choices); 1079 | $keypos_backend = array_search('backend', $data_choices); 1080 | 1081 | // Iterate through collected user data by row and column, build HTML table 1082 | for($row = 0; $row < sizeof($user_data); $row++) { 1083 | $table_user_data .= ""; 1084 | for($col = 0; $col < sizeof($user_data[$row]); $col++) { 1085 | $selected_data = $user_data[$row][$col]; 1086 | 1087 | $graphic_perc = null; 1088 | if($col === $keypos_percentage_used) { 1089 | if(is_numeric($selected_data)) { 1090 | $graphic_perc = "
"; 1091 | $pos_rel = " class='pos_rel'"; 1092 | } 1093 | if($selected_data === "N/A") 1094 | $selected_data = "N/A"; 1095 | else if($selected_data < $negligible_limit_percent) 1096 | $selected_data = "< ".$negligible_limit_percent." %"; 1097 | else 1098 | $selected_data .= " %"; 1099 | } 1100 | 1101 | $color_text = ($selected_data === "N/A" 1102 | || $selected_data === "< ".$negligible_limit_percent." %" 1103 | || $selected_data === "< ".$negligible_limit[0] 1104 | ." ".format_size($negligible_limit, 'return_unit')) 1105 | ? ' color: grey;' 1106 | : ' color: unset;'; 1107 | 1108 | $align = in_array($col, $keypos_right_align, true) 1109 | ? 'text-align: right; white-space: nowrap;' 1110 | : (in_array($col, $keypos_center_align, true) 1111 | ? 'text-align: center;' 1112 | : null); 1113 | 1114 | $table_user_data .= ""; 1116 | } 1117 | $table_user_data .= ''; 1118 | } 1119 | $table_user_data .= '
1115 | $graphic_perc$selected_data
'; 1120 | return $table_user_data_headers . $table_user_data; 1121 | } 1122 | 1123 | /** 1124 | * Build group table showing all associated userIDs and displaynames 1125 | * 1126 | * @return $table_group_data_headers concatenated with $table_group_data 1127 | * 1128 | */ 1129 | function build_table_group_data() { 1130 | $grouplist = $_SESSION['grouplist']; 1131 | 1132 | // Define HTML table and set header cell content 1133 | $table_group_data_headers = ''; 1134 | $table_group_data_headers .= 1135 | ' 1136 | 1138 | 1139 | 1140 | '; 1141 | 1142 | // Iterate through collected user data by row and column, build HTML table 1143 | for($row = 0; $row < sizeof($grouplist); $row++) { 1144 | $members = select_group_members($grouplist[$row]); 1145 | 1146 | // Check if group has no users associated, else list them as CSV 1147 | if($members === null) { 1148 | $user_ids = '-'; 1149 | $members_count = 0; 1150 | } else { 1151 | $user_ids = build_csv_line(array_column($members, 0), false, ', '); 1152 | $members_count = count($members); 1153 | } 1154 | 1155 | $user_displaynames = $members === null 1156 | ? '-' 1157 | : build_csv_line(array_column($members, 1), false, ', '); 1158 | 1159 | $table_group_data .= " 1160 | 1161 | "; 1162 | } 1163 | $table_group_data .= '
'.L10N_GROUP.''.L10N_USERS 1137 | .''.L10N_USER_ID.''.L10N_DISPLAYNAME.'
".$grouplist[$row]."$members_count$user_ids$user_displaynames
'; 1164 | return $table_group_data_headers . $table_group_data; 1165 | } 1166 | 1167 | /** 1168 | * Build groupfolder table 1169 | * 1170 | * @return $table_groupfolder_data_headers concatenated with $table_groupfolder_data 1171 | * 1172 | */ 1173 | function build_table_groupfolder_data() { 1174 | 1175 | require 'config.php'; 1176 | 1177 | // Define HTML table and set header cell content 1178 | $align_right = 'style="text-align: right;"'; 1179 | $align_center = 'style="text-align: center;"'; 1180 | 1181 | $table_groupfolder_data_headers = ''; 1182 | $table_groupfolder_data_headers .= 1183 | ' 1184 | 1185 | 1186 | 1187 | 1188 | 1189 | 1190 | 1191 | '; 1192 | 1193 | // Iterate through collected user data by row and column, build HTML table 1194 | foreach($_SESSION['raw_groupfolders_data']['ocs']['data'] as $groupfolder) { 1195 | 1196 | $groups = build_csv_line($groupfolder['groups'], true, ', '); 1197 | 1198 | $manager = build_csv_line($groupfolder['manage'], false, ', ', 'id', 'type'); 1199 | 1200 | $acl = $groupfolder['acl'] 1201 | ? "" 1202 | : null; 1203 | 1204 | $perc_used = $groupfolder['quota'] == -3 1205 | ? "N/A" 1206 | : round($groupfolder['size'] / $groupfolder['quota'] * 100); 1207 | 1208 | $perc_style = round($groupfolder['size'] / $groupfolder['quota'] * 100); 1209 | 1210 | if($perc_used < $negligible_limit_percent) 1211 | $perc_used = "< ".$negligible_limit_percent." %"; 1212 | elseif ($perc_used !== "N/A") 1213 | $perc_used .= " %"; 1214 | 1215 | $color_text_perc = ($perc_used === "N/A" 1216 | || $perc_used === "< ".$negligible_limit_percent." %") 1217 | ? " style='color: grey;'" 1218 | : ""; 1219 | 1220 | $color_text_size = 1221 | $groupfolder['size'] < format_size($negligible_limit,'return_raw') 1222 | ? " style='color: grey;'" 1223 | : ""; 1224 | 1225 | $table_groupfolder_data .= " 1226 | 1227 | 1228 | 1230 | 1233 | 1234 | 1235 | "; 1236 | } 1237 | $table_groupfolder_data .= "
'.L10N_ID.''.L10N_NAME.''.L10N_GROUPS.''.L10N_QUOTA_USED.''.L10N_PERCENTAGE_USED.''.L10N_QUOTA.''.L10N_ACL.''.L10N_ADMIN.'
{$groupfolder['id']}{$groupfolder['mount_point']}$groups".format_size($groupfolder['size'])." 1229 | 1231 |
$perc_used 1232 |
".format_size($groupfolder['quota'],'no_filter')."$acl$manager
"; 1238 | 1239 | return $table_groupfolder_data_headers . $table_groupfolder_data; 1240 | } 1241 | 1242 | function filter_email() { 1243 | 1244 | $uids_g = $_POST['recipients'] == 'group' 1245 | ? select_data_all_users_filter('groups', $_POST['group_selected']) 1246 | : $_SESSION['userlist']; 1247 | 1248 | $uids_l = $_POST['select_limit_login'] 1249 | ? select_data_all_users_filter('lastLogin', $_POST['lastlogin_since'], 1250 | $_POST['lastlogin_before']) 1251 | : $_SESSION['userlist']; 1252 | 1253 | $uids_q = $_POST['select_limit_quota'] 1254 | ? $user_ids_quota = select_data_all_users_filter('used',$_POST['quota_used']) 1255 | : $_SESSION['userlist']; 1256 | 1257 | $user_ids = array_intersect($_SESSION['userlist'], $uids_g, $uids_l, $uids_q); 1258 | 1259 | if(!$user_ids) { 1260 | header('Content-Type: text/html; charset=utf-8'); 1261 | exit('No users found matching these filter settings'); 1262 | } 1263 | 1264 | header("Location: ".build_mailto_list($_SESSION['message_mode'], $user_ids)); 1265 | 1266 | } 1267 | 1268 | /** 1269 | * Build 'mailto:' string 1270 | * 1271 | * Creates a string in 'mailto:' notation containing all user emails 1272 | * 1273 | * @param $mode Send emails as 'to', 'cc' or 'bcc' 1274 | * OPTIONAL DEFAULT: 'bcc' 1275 | * 1276 | * @return $mailto_list Exported emails as mailto: string for mass mailing 1277 | * 1278 | */ 1279 | function build_mailto_list($message_mode = 'bcc', $userlist = null) { 1280 | 1281 | $user_data = $_SESSION['raw_user_data']; 1282 | 1283 | // Initiate construction of mailto string, setting 'to:', 'cc:' or 'bcc:' 1284 | $mailto_list = "mailto:?$message_mode="; 1285 | 1286 | if(!$userlist) { 1287 | 1288 | // Iterate through user data and add email addresses 1289 | foreach($user_data as $key => $item) { 1290 | $user_email = $item['ocs']['data']['email']; 1291 | if ($user_email == 'N/A') 1292 | continue; 1293 | if ($key == 0) 1294 | $mailto_list .= $user_email; 1295 | else 1296 | $mailto_list .= ',' . $user_email; 1297 | } 1298 | 1299 | } 1300 | else { 1301 | foreach($user_data as $key => $item) { 1302 | if(in_array($item['ocs']['data']['id'], $userlist)) { 1303 | $user_email = $item['ocs']['data']['email']; 1304 | if ($user_email == 'N/A') 1305 | continue; 1306 | if ($key == 0) 1307 | $mailto_list .= $user_email; 1308 | else 1309 | $mailto_list .= ',' . $user_email; 1310 | } 1311 | } 1312 | } 1313 | 1314 | return $mailto_list; 1315 | 1316 | } 1317 | 1318 | /** 1319 | * CSV string creation 1320 | * 1321 | * Build CSV formatted string containing the user data 1322 | * 1323 | * @param $data Build a comma separated string from supplied array 1324 | * @param $delimiter Delimiter to be used between data columns 1325 | * 1326 | * @return $csv_user_data CSV formatted string containing the processed data 1327 | * 1328 | */ 1329 | function build_csv_user_data($data, $delimiter = ',') { 1330 | // Add headers to $csv_user_data variable 1331 | $csv_user_data .= build_csv_line(null, false, $delimiter)."
"; 1332 | 1333 | // Iterate through collected user data by row and column, build CSV output 1334 | for($row = 0; $row < sizeof($data); $row++) { 1335 | for($col = 0; $col < sizeof($data[$row]); $col++) { 1336 | // To prevent possible import issues, quote data cells of type string containing spaces 1337 | if(is_string($data[$row][$col])) { 1338 | if($data[$row][$col] != trim($data[$row][$col])) { 1339 | $csv_user_data .= '"'.$data[$row][$col].'"'; 1340 | } else { $csv_user_data .= $data[$row][$col]; } 1341 | } else { $csv_user_data .= $data[$row][$col]; } 1342 | // Put column separators between cells but not at the end of a record 1343 | if ($col < sizeof($data[$row])-1) 1344 | $csv_user_data .= $delimiter; 1345 | } 1346 | // Indicate the start of a new record 1347 | $csv_user_data .= "
"; 1348 | } 1349 | return $csv_user_data; 1350 | } 1351 | 1352 | /** 1353 | * Build array or CSV formatted string containing the group and user data 1354 | * 1355 | * @param $array Return an array or CSV 1356 | * OPTIONAL DEFAULT = 'null' 1357 | * @param $format Whether to return csv formatted data ('csv') or not 1358 | * OPTIONAL DEFAULT = 'null' 1359 | * 1360 | * @return $group_data Array or CSV formatted string containing the group associated user data 1361 | * 1362 | */ 1363 | function build_group_data($array = null, $format = null, $delimiter = ', ') { 1364 | $grouplist = $_SESSION['grouplist']; 1365 | 1366 | // Add headers to $group_data variable 1367 | if(!$array) 1368 | $group_data .= L10N_CSV_GROUP_HEADERS."
"; 1369 | 1370 | // Iterate through collected group data by row and column, build CSV output 1371 | for($row = 0; $row < sizeof($grouplist); $row++) { 1372 | $members = select_group_members($grouplist[$row], $format); 1373 | 1374 | // Check if group has no users associated, else list them as CSV 1375 | $user_ids = $members === null 1376 | ? '-' 1377 | : build_csv_line(array_column($members, 0), false, $delimiter); 1378 | 1379 | $user_displaynames = $members === null 1380 | ? '-' 1381 | : build_csv_line(array_column($members, 1), false, $delimiter); 1382 | 1383 | if($array == 'array') 1384 | $group_data[$row+1] = [$grouplist[$row], $user_ids, $user_displaynames]; 1385 | else 1386 | $group_data .= $grouplist[$row].$delimiter.'"'.$user_ids.'"' 1387 | .$delimiter.'"'.$user_displaynames.'"
'; 1388 | } 1389 | return $group_data; 1390 | } 1391 | 1392 | /** 1393 | * Build array or CSV formatted string containing the group and user data 1394 | * 1395 | * @param $array Return an array or CSV 1396 | * OPTIONAL DEFAULT = 'null' 1397 | * 1398 | * @return $groupfolder_return_data Array or CSV formatted string containing groupfolder data 1399 | * 1400 | */ 1401 | function build_groupfolder_data($array = null) { 1402 | if(!$array) 1403 | // Add headers to $group_data variable 1404 | $groupfolder_return_data .= L10N_ID.','.L10N_NAME.','.L10N_GROUPS.',' 1405 | .L10N_QUOTA_USED.','.L10N_PERCENTAGE_USED.','.L10N_QUOTA.','.L10N_ACL.',' 1406 | .L10N_ADMIN.'
'; 1407 | 1408 | // Iterate through collected groupfolder data, build CSV output or array 1409 | foreach($_SESSION['raw_groupfolders_data']['ocs']['data'] as $groupfolder) { 1410 | $groups = build_csv_line($groupfolder['groups'], true, ', '); 1411 | 1412 | $manager = build_csv_line($groupfolder['manage'], false, ', ', 'id', 'type'); 1413 | 1414 | if(!$array) 1415 | $acl = ($groupfolder['acl']) 1416 | ? '' 1417 | : null; 1418 | else 1419 | $acl = $groupfolder['acl']; 1420 | 1421 | $percent_used = round($groupfolder['size'] / $groupfolder['quota'] * 100, 1); 1422 | 1423 | $groupfolder_data = [$groupfolder['id'],$groupfolder['mount_point'], 1424 | $groups,format_size($groupfolder['size']),$percent_used, 1425 | format_size($groupfolder['quota']),$acl,$manager]; 1426 | 1427 | if(!$array) 1428 | $groupfolder_return_data .= build_csv_line($groupfolder_data)."
"; 1429 | else 1430 | $groupfolder_return_data[] = $groupfolder_data; 1431 | } 1432 | return $groupfolder_return_data; 1433 | } 1434 | 1435 | /** 1436 | * Build a comma separated line from a given array 1437 | * 1438 | * @param $array Array to build from 1439 | * OPTIONAL DEFAULT: null (session variable 'data_choices') 1440 | * @param $return_key Return array key instead of item 1441 | * OPTIONAL DEFAULT: false 1442 | * @param $delimiter Which char to put between cells 1443 | * OPTIONAL DEFAULT: ',' 1444 | * 1445 | * @return $csv_line CSV formatted string 1446 | * 1447 | */ 1448 | function build_csv_line($array = null, $return_key = false, $delimiter = ',', 1449 | $subarray_id = null, $subarray_type = null) { 1450 | 1451 | $array = $array ?? $_SESSION['data_choices']; 1452 | 1453 | $i = 0; 1454 | foreach($array as $key => $item) { 1455 | 1456 | if($subarray_id) 1457 | $item = $subarray_type 1458 | ? "{$item[$subarray_id]} ({$item[$subarray_type]})" 1459 | : $item[$subarray_id]; 1460 | 1461 | 1462 | if($return_key) 1463 | $csv_line .= ($i === 0) 1464 | ? $key 1465 | : $delimiter.$key; 1466 | else 1467 | $csv_line .= ($i === 0) 1468 | ? $item 1469 | : $delimiter.$item; 1470 | $i++; 1471 | 1472 | } 1473 | return $csv_line; 1474 | 1475 | } 1476 | 1477 | /** 1478 | * Source of the following function 'format_size': 1479 | * https://stackoverflow.com/questions/15188033/human-readable-file-size 1480 | * 1481 | * Convert byte size descriptors shown as integers to a human readable format 1482 | * 1483 | * slightly adapted 1484 | * 1485 | */ 1486 | function format_size($value, $option = null) { 1487 | 1488 | require 'config.php'; 1489 | 1490 | $s = array("B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"); 1491 | 1492 | if($option === 'return_unit') 1493 | return $s[$value[1]]; 1494 | 1495 | if($option === 'return_raw') 1496 | return $value[0] * pow(1024, $value[1]); 1497 | 1498 | if($option !== 'no_filter') 1499 | // "ignore"/filter sizes < 10 MiB (equals 10240 KB), return '< 10 MiB' 1500 | if($value < $negligible_limit[0]*pow(1024, $negligible_limit[1]) 1501 | && $value !== null) 1502 | return "< ".$negligible_limit[0]." ".$s[$negligible_limit[1]]; 1503 | 1504 | // Return '-' if value is not a number 1505 | if($value === null) 1506 | return "-"; 1507 | 1508 | // Return '0.0 MiB' to avoid 'division by zero' error 1509 | if($value === 0) 1510 | return '0.0 MiB'; 1511 | 1512 | // Return infinite sign, if value is -3 (Nextclouds API response for infinite quota) 1513 | if($value == -3) 1514 | return "∞ GB"; 1515 | 1516 | $e = floor(log($value, 1024)); 1517 | 1518 | return number_format(round($value/pow(1024, $e), 1),1).' '.$s[$e]; 1519 | } 1520 | 1521 | function set_security_headers() { 1522 | 1523 | include 'config.php'; 1524 | 1525 | header("X-Content-Type-Options: nosniff"); 1526 | header("Content-Security-Policy: frame-ancestors 'self' $frame_ancestors"); 1527 | header("X-Robots-Tag: none"); 1528 | header("Referrer-Policy: same-origin"); 1529 | 1530 | } 1531 | 1532 | function session_secure_start() { 1533 | 1534 | session_set_cookie_params( 1535 | '3600', '/', $_SERVER['SERVER_NAME'], isset($_SERVER["HTTPS"]), true); 1536 | session_start(); 1537 | 1538 | } 1539 | 1540 | function logout() { 1541 | 1542 | unset($_SESSION['data_choices']); 1543 | unset($_SESSION['userlist']); 1544 | unset($_SESSION['grouplist']); 1545 | unset($_SESSION['raw_user_data']); 1546 | unset($_SESSION['raw_groupfolders_data']); 1547 | unset($_SESSION['user_name']); 1548 | unset($_SESSION['user_pass']); 1549 | unset($_SESSION['target_url']); 1550 | unset($_SESSION['quota_total_assigned']); 1551 | unset($_SESSION['quota_total_free']); 1552 | unset($_SESSION['quota_total_used']); 1553 | unset($_SESSION['quota_groupfolders_used']); 1554 | unset($_SESSION['quota_groupfolders_assigned']); 1555 | 1556 | session_destroy(); 1557 | session_write_close(); 1558 | setcookie(session_name(),'',0,'/'); 1559 | 1560 | header('Location: index.php'); 1561 | 1562 | } 1563 | 1564 | function check_and_set_filter($filter) { 1565 | 1566 | include 'config.php'; 1567 | 1568 | switch($filter) { 1569 | case 'group': 1570 | if($filter_group && !$_SESSION['group_filter_checked_by_config']) { 1571 | $_SESSION['group_filter_checked_by_config'] = true; 1572 | return " checked"; 1573 | } 1574 | $chosen_filter = 'filter_group_choice'; 1575 | break; 1576 | case 'lastLogin': 1577 | $chosen_filter = 'filter_lastLogin_choice'; 1578 | break; 1579 | case 'quota': 1580 | $chosen_filter = 'filter_quota_choice'; 1581 | break; 1582 | } 1583 | 1584 | if(!$_SESSION['filters_set']) 1585 | return; 1586 | 1587 | if(in_array($chosen_filter, $_SESSION['filters_set'])) 1588 | return " checked"; 1589 | 1590 | } 1591 | -------------------------------------------------------------------------------- /groupfolders.php: -------------------------------------------------------------------------------- 1 | "; 12 | 13 | ?> 14 | 15 | 16 | 17 | 18 | Nextcloud Userexport 19 | 20 | 21 | 22 | '.L10N_CONNECTION_NEEDED); 29 | } 30 | 31 | print_status_overview(); 32 | 33 | ?> 34 | 35 |
36 |
37 | 38 | 39 | 40 |

41 | 43 |


44 | 45 | > 48 | > 51 |

52 | 54 |
55 | 56 | 57 | -------------------------------------------------------------------------------- /groupfolders_detail.php: -------------------------------------------------------------------------------- 1 | "; 33 | 34 | ?> 35 | 36 | 37 | 38 | 39 | Nextcloud Userexport 40 | 63 | 64 | 65 | 66 | '.L10N_CONNECTION_NEEDED); 73 | } 74 | 75 | print_status_overview(); 76 | 77 | /** 78 | * Display results page either as HTML table or comma separated values (CSV) 79 | */ 80 | if($export_type == 'table') 81 | echo build_table_groupfolder_data(); 82 | else 83 | echo build_groupfolder_data(); 84 | 85 | ?> 86 | 87 | 88 | -------------------------------------------------------------------------------- /groups.php: -------------------------------------------------------------------------------- 1 | "; 12 | 13 | ?> 14 | 15 | 16 | 17 | 18 | Nextcloud Userexport 19 | 20 | 21 | 22 | '.L10N_CONNECTION_NEEDED); 29 | } 30 | 31 | print_status_overview(); 32 | 33 | ?> 34 | 35 |
36 |
37 | 38 | 39 | 40 |

41 | 43 |


44 | 45 | > 48 | > 51 |

52 | 54 |
55 | 56 | 57 | -------------------------------------------------------------------------------- /groups_detail.php: -------------------------------------------------------------------------------- 1 | "; 34 | 35 | ?> 36 | 37 | 38 | 39 | 40 | Nextcloud Userexport 41 | 64 | 65 | 66 | 67 | '.L10N_CONNECTION_NEEDED); 74 | } 75 | 76 | print_status_overview(); 77 | 78 | /** 79 | * Display results page either as HTML table or comma separated values (CSV) 80 | */ 81 | if($export_type == 'table') 82 | echo build_table_group_data(); 83 | else 84 | echo build_group_data(null, null, ','); 85 | 86 | ?> 87 | 88 | 89 | -------------------------------------------------------------------------------- /index.php: -------------------------------------------------------------------------------- 1 |
Token needs to be supplied through GET parameter e.g. https://export.cloud.example.com?access_token=tokengoeshere and is set in config.php 42 |
(This has nothing to do with your Nextcloud user credentials)'); 43 | } 44 | 45 | } 46 | 47 | /** 48 | * Check if data choices have been submitted (GET parameter 'select'), 49 | * if yes set $_SESSION variable to GET values, else set defaults 50 | */ 51 | $_SESSION['data_choices'] = isset($_GET["select"]) 52 | ? explode(",", $_GET["select"]) 53 | : $data_choices; 54 | // Check if export type has been set (GET parameter 'type'), else default to 'table' 55 | $_SESSION['export_type'] = $_GET['type'] ?? 'table'; 56 | // Check if message mode has been set (GET parameter 'msg_mode'), else default to 'bcc' 57 | $_SESSION['message_mode'] = $_GET['msg_mode'] ?? 'bcc'; 58 | // Check if group has been selected for filtering 59 | $_SESSION['filter_group'] = $_GET['filter_group'] ?? $filter_group; 60 | 61 | // Populate session array 'data_options' with all data options that can be selected 62 | set_data_options(); 63 | 64 | if($_SERVER['REQUEST_METHOD'] == 'POST') { 65 | 66 | // Transfer $_POST values to $_SESSION variables for further use 67 | $_SESSION['user_name'] = $_POST['user_name']; 68 | $_SESSION['user_pass'] = $_POST['user_pass']; 69 | 70 | // Save the script's start timestamp to measure execution time 71 | $_SESSION['timestamp_script_start'] = microtime(true); 72 | 73 | /** 74 | * Check if plain HTTP is used without override command and exit if not 75 | * add 'https://' if no protocol specified and set $_SESSION variable for further use 76 | */ 77 | $_SESSION['target_url'] = check_https($_POST['target_url']); 78 | 79 | // cURL API call fetching userlist (containing only user IDs) from target server 80 | fetch_userlist(); 81 | // cURL API call fetching grouplist (containing only group names) from target server 82 | fetch_grouplist(); 83 | // cURL API call fetching groupfolder data from target server 84 | fetch_raw_groupfolders_data(); 85 | 86 | // Count list items and save them as session variables 87 | $_SESSION['user_count'] = count($_SESSION['userlist']); 88 | $_SESSION['group_count'] = count($_SESSION['grouplist']); 89 | 90 | /** 91 | * Count list items and save them as session variable 92 | * (if groupfolders app is active and at least one groupfolder exists) 93 | */ 94 | $_SESSION['groupfolders_count'] = 95 | $_SESSION['groupfolders_active'] == true 96 | ? count($_SESSION['raw_groupfolders_data']['ocs']['data']) 97 | : null; 98 | 99 | // Fetch all user details (this can take a long time) 100 | $_SESSION['raw_user_data'] = fetch_raw_user_data(); 101 | 102 | // Calculate how much disk space is assigned, used and available in total 103 | calculate_quota(); 104 | 105 | } 106 | 107 | set_security_headers(); 108 | 109 | // Tell the browser which language is used 110 | echo ""; 111 | 112 | ?> 113 | 114 | 115 | 116 | 117 | Nextcloud Userexport 118 | 119 | 144 | 145 | 146 | 147 | 148 | 155 |
156 |
157 |
158 | 159 |

160 | 161 | 162 | 167 | 168 | 172 | 176 | 178 | "; 182 | ?> 183 | 184 |
163 | 166 |
171 | 175 |
179 | 181 |
185 |
186 | 188 |
189 |
190 |
191 | 192 | 193 | -------------------------------------------------------------------------------- /l10n/de.php: -------------------------------------------------------------------------------- 1 | verbinden!'); 13 | define('L10N_SELECT_USER_DATA', 'Folgende Spalten miteinbeziehen:'); 14 | define('L10N_TABLE', 'Tabelle'); 15 | define('L10N_CSV', 'CSV'); 16 | define('L10N_DISPLAY', 'Anzeigen'); 17 | define('L10N_DOWNLOAD_CSV', 'Herunterladen (CSV)'); 18 | define('L10N_FORMAT_AS', 'Formatieren als:'); 19 | define('L10N_HTTP_IS_BLOCKED', 'Die Nutzung von ungesichertem HTTP ist aus Sicherheitsgründen blockiert.'); 20 | define('L10N_HTTPS_RECOMMENDATION', 'Bitte stattdessen HTTPS benutzen.'); 21 | define('L10N_HTTPS_OVERRIDE_HINT', 'Die Zugangsdaten können - sofern nötig - unverschlüsselt gesendet werden, indem ein \'!\' vor \'http\' eingefügt wird.'); 22 | define('L10N_EG', 'z.B. '); 23 | define('L10N_ERROR_CURL_CONNECTION', 'Bitte die angegebene Server Adresse und die Netzwerkverbindung überprüfen'); 24 | define('L10N_ERROR', 'Fehler: '); 25 | define('L10N_ERROR_URL', 'Bitte die angegebene Server Adresse überprüfen'); 26 | define('L10N_ERROR_EMPTY_API_RESPONSE', 'Die API Antwort enthielt keine Daten'); 27 | define('L10N_CONNECTED_TO_SERVER', 'Verbunden mit Server: '); 28 | define('L10N_TOTAL', 'Gesamt: '); 29 | define('L10N_ACCESS_TO_ALL_MENU_OPTIONS', 'Alle Menü Optionen sind ab jetzt verfügbar'); 30 | define('L10N_SEND_EMAIL_TO_ALL_USERS', 'E-Mails an alle Benutzer versenden'); 31 | define('L10N_GROUP', 'Gruppe'); 32 | define('L10N_USER_ID', 'Benutzer ID'); 33 | define('L10N_DISPLAYNAME', 'Anzeigename'); 34 | define('L10N_CSV_GROUP_HEADERS', 'Gruppe,Benutzer ID,Anzeigename'); 35 | define('L10N_DOWNLOADED', 'Heruntergeladen:'); 36 | define('L10N_USERS_AND', 'Benutzer und'); 37 | define('L10N_GROUPS_IN', 'Gruppen in'); 38 | define('L10N_SECONDS', 'Sekunden'); 39 | define('L10N_LAST_LOGIN', 'Letzte Anmeldung'); 40 | define('L10N_BACKEND', 'Backend'); 41 | define('L10N_ENABLED', 'Aktiviert'); 42 | define('L10N_QUOTA', 'Kontingent'); 43 | define('L10N_QUOTA_USED', 'Genutzt'); 44 | define('L10N_QUOTA_FREE', 'Verfügbar'); 45 | define('L10N_SUBADMIN', 'Subadmin'); 46 | define('L10N_LANGUAGE', 'Sprache'); 47 | define('L10N_LOCALE', 'Gebietsschema'); 48 | define('L10N_TOGGLE_ALL', 'Alle umschalten'); 49 | define('L10N_SELECT_AT_LEAST_ONE_COLUMN', 'Mindestens eine Spalte muss ausgewählt werden.'); 50 | define('L10N_RETURN_TO_FORM', 'Zurück zum Formular'); 51 | define('L10N_USER_DOES_NOT_EXIST', 'Benutzer existiert nicht'); 52 | define('L10N_AUTHENTICATION', 'Legitimation'); 53 | define('L10N_CHECK_USER_PASS', 'Bitte Benutzernamen und Passwort überprüfen'); 54 | define('L10N_HINT_ADMIN_OR_GROUP_ADMIN', 'Hinweis: Der angegebene Benutzer muss Administrator oder Gruppenadministrator sein'); 55 | define('L10N_UNKNOWN', 'unbekannt'); 56 | define('L10N_STATUSCODE', 'Statuscode: '); 57 | define('L10N_NUMBER_SIGN', '#'); 58 | define('L10N_GROUPFOLDERS', 'Gruppenordner'); 59 | define('L10N_ID', 'ID'); 60 | define('L10N_NAME', 'Name'); 61 | define('L10N_ACL', 'ACL'); 62 | define('L10N_ADMIN', 'Admin'); 63 | define('L10N_STATISTICS', 'Statistik'); 64 | define('L10N_PERCENTAGE_USED', 'Genutzt %'); 65 | define('L10N_LOGOUT', 'Abmelden'); 66 | define('L10N_NEXTCLOUD', 'Nextcloud'); 67 | define('L10N_VERSION', 'Version'); 68 | define('L10N_DATA_RETRIEVED', 'Daten abgerufen'); 69 | define('L10N_SEND_AS','Senden als:'); 70 | define('L10N_SEND_TO','Senden an:'); 71 | define('L10N_ALL_USERS','Alle Benutzer'); 72 | define('L10N_FILTER_BY','Filtern nach:'); 73 | define('L10N_FILTERED_BY','gefiltert nach'); 74 | define('L10N_SELECT_GROUP','Gruppe auswählen'); 75 | define('L10N_LAST_LOGIN_BETWEEN','Letzte Anmeldung zwischen'); 76 | define('L10N_AND','und'); 77 | define('L10N_DISK_SPACE','Speicherplatz'); 78 | define('L10N_YES','Ja'); 79 | define('L10N_NO','Nein'); 80 | define('L10N_COLUMN_HEADERS','Spaltenüberschriften'); 81 | define('L10N_CREATE_LIST','Liste erstellen'); 82 | define('L10N_FROM','von'); 83 | define('L10N_UNTIL','bis'); 84 | define('L10N_TODAY','heute'); 85 | define('L10N_EXECUTION_TIMES','Ausführungszeiten'); 86 | define('L10N_FETCH_USERLIST','Benutzerliste holen'); 87 | define('L10N_FETCH_GROUPLIST','Gruppenliste holen'); 88 | define('L10N_FETCH_GROUPFOLDERS','Gruppenordner holen'); 89 | define('L10N_FETCH_USERDATA','Benutzerdaten holen'); 90 | define('L10N_ACCESS_TOKEN','Zugangstoken'); 91 | define('L10N_HTTPS_STRICT_MODE','Die HTTPS_STRICT Option wurde ausgewählt, unverschlüsseltes http:// kann auch mit !http:// nicht erzwungen werden.
Diese Einstellung kann in der config.php Datei geändert werden.'); 92 | define('L10N_USED','genutzt'); 93 | define('L10N_ASSIGNED','zugeordnet'); 94 | define('L10N_FREE','frei'); 95 | -------------------------------------------------------------------------------- /l10n/en.php: -------------------------------------------------------------------------------- 1 | connect to a Nextcloud server first!'); 13 | define('L10N_SELECT_USER_DATA', 'Include the following columns:'); 14 | define('L10N_TABLE', 'Table'); 15 | define('L10N_CSV', 'CSV'); 16 | define('L10N_DISPLAY', 'Display'); 17 | define('L10N_DOWNLOAD_CSV', 'Download (CSV)'); 18 | define('L10N_FORMAT_AS', 'Format as:'); 19 | define('L10N_HTTP_IS_BLOCKED', 'The use of plain HTTP is blocked for security reasons.'); 20 | define('L10N_HTTPS_RECOMMENDATION', 'Please use HTTPS instead.'); 21 | define('L10N_HTTPS_OVERRIDE_HINT', 'You can send your admin credentials unencrypted - if you need to - by inserting \'!\' before \'http\'.'); 22 | define('L10N_EG', 'e.g. '); 23 | define('L10N_ERROR_CURL_CONNECTION', 'Please check provided URL and network connection'); 24 | define('L10N_ERROR', 'Error: '); 25 | define('L10N_ERROR_URL', 'Please check provided URL'); 26 | define('L10N_ERROR_EMPTY_API_RESPONSE', 'API response was empty'); 27 | define('L10N_CONNECTED_TO_SERVER', 'Connected to server: '); 28 | define('L10N_TOTAL', 'Total: '); 29 | define('L10N_ACCESS_TO_ALL_MENU_OPTIONS', 'You can now access all menu options'); 30 | define('L10N_SEND_EMAIL_TO_ALL_USERS', 'Send email to all users'); 31 | define('L10N_GROUP', 'Group'); 32 | define('L10N_USER_ID', 'User ID'); 33 | define('L10N_DISPLAYNAME', 'Displayname'); 34 | define('L10N_SUBJECT_ALL_USER_MAIL', 'All%20user%20mail'); 35 | define('L10N_CSV_GROUP_HEADERS', 'group,loginID,displayname'); 36 | define('L10N_DOWNLOADED', 'Downloaded:'); 37 | define('L10N_USERS_AND', 'users and'); 38 | define('L10N_GROUPS_IN', 'groups in'); 39 | define('L10N_SECONDS', 'seconds'); 40 | define('L10N_LAST_LOGIN', 'Last login'); 41 | define('L10N_BACKEND', 'Backend'); 42 | define('L10N_ENABLED', 'Enabled'); 43 | define('L10N_QUOTA', 'Quota'); 44 | define('L10N_QUOTA_USED', 'Quota used'); 45 | define('L10N_QUOTA_FREE', 'Quota free'); 46 | define('L10N_SUBADMIN', 'Subadmin'); 47 | define('L10N_LANGUAGE', 'Language'); 48 | define('L10N_LOCALE', 'Locale'); 49 | define('L10N_TOGGLE_ALL', 'Toggle all'); 50 | define('L10N_SELECT_AT_LEAST_ONE_COLUMN', 'At least one column needs to be selected.'); 51 | define('L10N_RETURN_TO_FORM', 'Return to form'); 52 | define('L10N_USER_DOES_NOT_EXIST', 'User does not exist'); 53 | define('L10N_AUTHENTICATION', 'Authentication'); 54 | define('L10N_CHECK_USER_PASS', 'Please check username and password'); 55 | define('L10N_HINT_ADMIN_OR_GROUP_ADMIN', 'Hint: The provided user needs to be admin or group admin'); 56 | define('L10N_UNKNOWN', 'unknown'); 57 | define('L10N_STATUSCODE', 'Statuscode: '); 58 | define('L10N_NUMBER_SIGN', '#'); 59 | define('L10N_GROUPFOLDERS', 'Groupfolders'); 60 | define('L10N_ID', 'ID'); 61 | define('L10N_NAME', 'Name'); 62 | define('L10N_ACL', 'ACL'); 63 | define('L10N_ADMIN', 'Admin'); 64 | define('L10N_STATISTICS', 'Statistics'); 65 | define('L10N_PERCENTAGE_USED', '% used'); 66 | define('L10N_LOGOUT', 'Logout'); 67 | define('L10N_NEXTCLOUD', 'Nextcloud'); 68 | define('L10N_VERSION', 'Version'); 69 | define('L10N_DATA_RETRIEVED', 'Data retrieved'); 70 | define('L10N_SEND_AS','Send as:'); 71 | define('L10N_SEND_TO','Send to:'); 72 | define('L10N_ALL_USERS','All users'); 73 | define('L10N_FILTER_BY','Filter by:'); 74 | define('L10N_FILTERED_BY','filtered by'); 75 | define('L10N_SELECT_GROUP','select group'); 76 | define('L10N_LAST_LOGIN_BETWEEN','Last login between'); 77 | define('L10N_AND','and'); 78 | define('L10N_DISK_SPACE','Disk space'); 79 | define('L10N_YES','Yes'); 80 | define('L10N_NO','No'); 81 | define('L10N_COLUMN_HEADERS','Column headers'); 82 | define('L10N_CREATE_LIST','Create list'); 83 | define('L10N_FROM','from'); 84 | define('L10N_UNTIL','until'); 85 | define('L10N_TODAY','today'); 86 | define('L10N_EXECUTION_TIMES','Execution times'); 87 | define('L10N_FETCH_USERLIST','Fetch userlist'); 88 | define('L10N_FETCH_GROUPLIST','Fetch grouplist'); 89 | define('L10N_FETCH_GROUPFOLDERS','Fetch groupfolders'); 90 | define('L10N_FETCH_USERDATA','Fetch userdata'); 91 | define('L10N_ACCESS_TOKEN','Access token'); 92 | define('L10N_HTTPS_STRICT_MODE','HTTPS STRICT MODE option is active, plain http:// cannot be used.
This can be configured in config.php.'); 93 | define('L10N_USED','used'); 94 | define('L10N_ASSIGNED','assigned'); 95 | define('L10N_FREE','free'); 96 | -------------------------------------------------------------------------------- /l10n/language_iso_codes.txt: -------------------------------------------------------------------------------- 1 | Language ISO Code 2 | Abkhazian ab 3 | Afar aa 4 | Afrikaans af 5 | Akan ak 6 | Albanian sq 7 | Amharic am 8 | Arabic ar 9 | Aragonese an 10 | Armenian hy 11 | Assamese as 12 | Avaric av 13 | Avestan ae 14 | Aymara ay 15 | Azerbaijani az 16 | Bambara bm 17 | Bashkir ba 18 | Basque eu 19 | Belarusian be 20 | Bengali (Bangla) bn 21 | Bihari bh 22 | Bislama bi 23 | Bosnian bs 24 | Breton br 25 | Bulgarian bg 26 | Burmese my 27 | Catalan ca 28 | Chamorro ch 29 | Chechen ce 30 | Chichewa, Chewa, Nyanja ny 31 | Chinese zh 32 | Chinese (Simplified) zh-Hans 33 | Chinese (Traditional) zh-Hant 34 | Chuvash cv 35 | Cornish kw 36 | Corsican co 37 | Cree cr 38 | Croatian hr 39 | Czech cs 40 | Danish da 41 | Divehi, Dhivehi, Maldivian dv 42 | Dutch nl 43 | Dzongkha dz 44 | English en 45 | Esperanto eo 46 | Estonian et 47 | Ewe ee 48 | Faroese fo 49 | Fijian fj 50 | Finnish fi 51 | French fr 52 | Fula, Fulah, Pulaar, Pular ff 53 | Galician gl 54 | Gaelic (Scottish) gd 55 | Gaelic (Manx) gv 56 | Georgian ka 57 | German de 58 | Greek el 59 | Greenlandic kl 60 | Guarani gn 61 | Gujarati gu 62 | Haitian Creole ht 63 | Hausa ha 64 | Hebrew he 65 | Herero hz 66 | Hindi hi 67 | Hiri Motu ho 68 | Hungarian hu 69 | Icelandic is 70 | Ido io 71 | Igbo ig 72 | Indonesian id, in 73 | Interlingua ia 74 | Interlingue ie 75 | Inuktitut iu 76 | Inupiak ik 77 | Irish ga 78 | Italian it 79 | Japanese ja 80 | Javanese jv 81 | Kalaallisut, Greenlandic kl 82 | Kannada kn 83 | Kanuri kr 84 | Kashmiri ks 85 | Kazakh kk 86 | Khmer km 87 | Kikuyu ki 88 | Kinyarwanda (Rwanda) rw 89 | Kirundi rn 90 | Kyrgyz ky 91 | Komi kv 92 | Kongo kg 93 | Korean ko 94 | Kurdish ku 95 | Kwanyama kj 96 | Lao lo 97 | Latin la 98 | Latvian (Lettish) lv 99 | Limburgish ( Limburger) li 100 | Lingala ln 101 | Lithuanian lt 102 | Luga-Katanga lu 103 | Luganda, Ganda lg 104 | Luxembourgish lb 105 | Manx gv 106 | Macedonian mk 107 | Malagasy mg 108 | Malay ms 109 | Malayalam ml 110 | Maltese mt 111 | Maori mi 112 | Marathi mr 113 | Marshallese mh 114 | Moldavian mo 115 | Mongolian mn 116 | Nauru na 117 | Navajo nv 118 | Ndonga ng 119 | Northern Ndebele nd 120 | Nepali ne 121 | Norwegian no 122 | Norwegian bokmål nb 123 | Norwegian nynorsk nn 124 | Nuosu ii 125 | Occitan oc 126 | Ojibwe oj 127 | Old Church Slavonic, Old Bulgarian cu 128 | Oriya or 129 | Oromo (Afaan Oromo) om 130 | Ossetian os 131 | Pāli pi 132 | Pashto, Pushto ps 133 | Persian (Farsi) fa 134 | Polish pl 135 | Portuguese pt 136 | Punjabi (Eastern) pa 137 | Quechua qu 138 | Romansh rm 139 | Romanian ro 140 | Russian ru 141 | Sami se 142 | Samoan sm 143 | Sango sg 144 | Sanskrit sa 145 | Serbian sr 146 | Serbo-Croatian sh 147 | Sesotho st 148 | Setswana tn 149 | Shona sn 150 | Sichuan Yi ii 151 | Sindhi sd 152 | Sinhalese si 153 | Siswati ss 154 | Slovak sk 155 | Slovenian sl 156 | Somali so 157 | Southern Ndebele nr 158 | Spanish es 159 | Sundanese su 160 | Swahili (Kiswahili) sw 161 | Swati ss 162 | Swedish sv 163 | Tagalog tl 164 | Tahitian ty 165 | Tajik tg 166 | Tamil ta 167 | Tatar tt 168 | Telugu te 169 | Thai th 170 | Tibetan bo 171 | Tigrinya ti 172 | Tonga to 173 | Tsonga ts 174 | Turkish tr 175 | Turkmen tk 176 | Twi tw 177 | Uyghur ug 178 | Ukrainian uk 179 | Urdu ur 180 | Uzbek uz 181 | Venda ve 182 | Vietnamese vi 183 | Volapük vo 184 | Wallon wa 185 | Welsh cy 186 | Wolof wo 187 | Western Frisian fy 188 | Xhosa xh 189 | Yiddish yi, ji 190 | Yoruba yo 191 | Zhuang, Chuang za 192 | Zulu zu 193 | -------------------------------------------------------------------------------- /navigation.php: -------------------------------------------------------------------------------- 1 | 44 | -------------------------------------------------------------------------------- /statistics.php: -------------------------------------------------------------------------------- 1 | " 12 | 13 | ?> 14 | 15 | 16 | 17 | 18 | Nextcloud Userexport 19 | 20 | 21 | 22 | '.L10N_CONNECTION_NEEDED); 29 | } 30 | 31 | print_status_overview('full'); 32 | 33 | ?> 34 | 35 | 36 | -------------------------------------------------------------------------------- /style.php: -------------------------------------------------------------------------------- 1 | 37 | 38 | * { 39 | font-family: Helvetica, Arial, sans-serif; 40 | } 41 | 42 | body { 43 | background-color: ; 44 | } 45 | 46 | #navigation ul { 47 | list-style-type: none; 48 | margin: 0; 49 | padding: 0; 50 | overflow: hidden; 51 | background-color: ; 52 | } 53 | 54 | #navigation li { 55 | float: left; 56 | border-right: 1px solid #8097b9; 57 | } 58 | 59 | #navigation li:last-child { 60 | border-right: none; 61 | } 62 | 63 | #navigation li a { 64 | display: block; 65 | color: ; 66 | text-align: center; 67 | padding: 14px 16px; 68 | text-decoration: none; 69 | } 70 | 71 | #navigation #currentpage a { 72 | background: ; 73 | color: ; 74 | } 75 | 76 | #navigation li a:hover { 77 | background-color: ; 78 | color: ; 79 | } 80 | 81 | #button-connect { 82 | background-color: ; 83 | color: ; 84 | height: 45px; 85 | width: 300px; 86 | } 87 | 88 | #button-display { 89 | background-color: ; 90 | color: ; 91 | height: 45px; 92 | width: 300px; 93 | } 94 | 95 | #button-download { 96 | background-color: ; 97 | color: ; 98 | height: 45px; 99 | width: 300px; 100 | } 101 | 102 | #button-email { 103 | background-color: ; 104 | color: ; 105 | height: 45px; 106 | width: 300px; 107 | } 108 | 109 | input[type="radio"] { 110 | margin-top: -2px; 111 | vertical-align: middle; 112 | } 113 | 114 | input[type="checkbox"] { 115 | margin-top: -1px; 116 | vertical-align: middle; 117 | } 118 | 119 | table { 120 | border-collapse: collapse; 121 | } 122 | 123 | .no_show_link { 124 | text-decoration: none; 125 | color: black; 126 | } 127 | 128 | .align_r { 129 | text-align: right; 130 | } 131 | 132 | .align_c { 133 | text-align: center; 134 | } 135 | 136 | .list table, .list td, .list th { 137 | border: 1px solid #ddd; 138 | } 139 | 140 | .list th { 141 | text-align: left; 142 | background-color: ; 143 | color: ; 144 | padding: 8px 4px 4px; 145 | cursor: pointer; 146 | } 147 | 148 | .list td { 149 | padding: 4px 4px 0px; 150 | } 151 | 152 | .list tr:nth-child(even) { 153 | background-color: #f2f2f2; 154 | position: relative; 155 | z-index: -2; 156 | } 157 | 158 | #info_filters { 159 | margin-left: 1em; 160 | margin-top: 1em; 161 | } 162 | 163 | .pad_b { 164 | padding-bottom: 0.5em; 165 | } 166 | 167 | .pad_l { 168 | padding-left: 0.7em; 169 | } 170 | 171 | .red { 172 | color: red; 173 | } 174 | 175 | .status th, .status td { 176 | padding: 4px 4px 0px; 177 | } 178 | 179 | .status td:nth-child(2) { 180 | text-align: right; 181 | } 182 | 183 | .bg { 184 | position: absolute; 185 | height: 1.2em; 186 | margin: auto; 187 | right: 0; 188 | top: 0; 189 | bottom: 0; 190 | background: darkgreen; 191 | opacity: 0.65; 192 | z-index: -1; 193 | } 194 | 195 | .pos_rel { 196 | position: relative; 197 | } 198 | -------------------------------------------------------------------------------- /users.php: -------------------------------------------------------------------------------- 1 | "; 14 | 15 | ?> 16 | 17 | 18 | 19 | 20 | Nextcloud Userexport 21 | 29 | 30 | 31 | 32 | '.L10N_CONNECTION_NEEDED); 39 | } 40 | 41 | print_status_overview(); 42 | 43 | echo '
'.L10N_SELECT_USER_DATA.'

44 |
45 | 46 | '; 47 | 48 | foreach($_SESSION['data_options'] as $option => $title) { 49 | $checked = in_array($option, $_SESSION['data_choices']) 50 | ? " checked" 51 | : ""; 52 | switch ($option) { 53 | case 'email': 54 | case 'enabled': 55 | case 'percentage_used': 56 | case 'subadmin': 57 | case 'locale': 58 | echo ""; 59 | break; 60 | case 'lastLogin': 61 | case 'quota': 62 | case 'free': 63 | case 'language': 64 | echo ""; 65 | break; 66 | default: 67 | echo ""; 68 | } 69 | } 70 | 71 | echo " 72 | 76 |
$title
$title$title
73 | " 74 | .L10N_TOGGLE_ALL. " 75 |


77 | ".L10N_FILTER_BY."

78 | 79 | 80 | 93 | 94 | 114 | 115 | 116 | 134 | 135 |
81 | 83 | 84 | 92 |
95 | 97 | 98 | 104 | ".L10N_AND." 105 | 113 |
117 | 119 | 120 | 125 | 131 | GB 133 |
"; 136 | 137 | ?> 138 |

139 | 140 | > 143 | > CSV 146 |

147 | 149 |


150 | 151 | > 154 | > 157 |

158 | 160 |
161 | 162 | 163 | -------------------------------------------------------------------------------- /users_detail.php: -------------------------------------------------------------------------------- 1 | "; 55 | 56 | ?> 57 | 58 | 59 | 60 | 61 | Nextcloud Userexport 62 | 85 | 86 | 87 | 88 | '.L10N_CONNECTION_NEEDED); 95 | } 96 | 97 | print_status_overview(); 98 | 99 | /** 100 | * Display results page either as HTML table or comma separated values (CSV) 101 | */ 102 | if($export_type == 'table') 103 | echo build_table_user_data(select_data_all_users(null, $userlist)); 104 | else 105 | echo build_csv_user_data(select_data_all_users(null, $userlist, null, ',')); 106 | 107 | ?> 108 | 109 | 110 | --------------------------------------------------------------------------------