├── CHANGELOG.md ├── LICENSE ├── README.md ├── interface └── lib │ ├── classes │ └── validate_nextcloud.inc.php │ └── plugins │ ├── nextcloud_plugin.inc.php │ └── nextcloud_plugin │ ├── VERSION │ ├── lib │ └── lang │ │ └── en.lng │ ├── screenshots │ ├── ispconfig-01.png │ ├── ispconfig-02.png │ └── ispconfig-03.png │ ├── sql │ ├── mail_domain.sql │ └── mail_user.sql │ └── templates │ ├── mail_domain_edit.htm │ ├── mail_user_edit.htm │ └── server_config_edit.htm └── server └── plugins-available └── nextcloud_plugin.inc.php /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## [Unreleased] 2 | 3 | ## 29.0.1 4 | 5 | - Implement enable/disable login by server 6 | 7 | ## 29.0.0 8 | 9 | - Initial release 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 2-Clause License 2 | 3 | Copyright (c) 2024, Michael Epstein, Mediabox EIRL 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 11 | 2. Redistributions in binary form must reproduce the above copyright notice, 12 | this list of conditions and the following disclaimer in the documentation 13 | and/or other materials provided with the distribution. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 19 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 22 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ISPConfig - Nextcloud Plugin 2 | 3 | Allows users to log in to **Nextcloud** with their email or custom login name created in the **ISPConfig 3** Control Panel. 4 | 5 | **IMPORTANT:** This plugin requires the installation of an APP in **Nextcloud**. [Nextcloud - User ISPConfig API](https://github.com/mediabox-cl/nextcloud-user-ispconfig-api.git) 6 | 7 | ## Features 8 | 9 | This plugin has three places where you can configure different functionalities or integrations with **Nextcloud**. 10 | 11 | - `System > Server Config > Your Server Name > Nextcloud (TAB)` 12 | - `Email > Domain > Your Domain Name > Nextcloud (TAB)` 13 | - `Email > Email Mailbox > Your Mailbox Name > Nextcloud (TAB)` 14 | 15 | ### Users 16 | 17 | - Users can change their **ISPConfig** password and display name in the **Nextcloud** interface. 18 | - Delete the **Nextcloud** user account and data when the **ISPConfig** Mailbox User or Domain is deleted. 19 | - Enable/Disable login by user or domain. 20 | - You can define the user quota per domain or per user. 21 | - ... 22 | 23 | ### Groups 24 | 25 | Groups have 3 places where you can set them; Server, Domain and User. 26 | - The Server Group is like a Global Group for all the mail users in this server. 27 | - The Domain Group is a Group for all the Users ho belong to each domain. 28 | - The User Group are specific for each user. 29 | - Add or remove users to the group defined in Server, Domain and User. 30 | - Add or remove users as admin of the groups they belong to. 31 | - Delete empty group. 32 | - ... 33 | 34 | ## Installation 35 | 36 | ### Manual installation 37 | 38 | Installation in **ISPConfig** is a bit tricky because this control panel, despite being very good, is not very user-friendly when it comes to creating/installing plugins. 39 | 40 | **IMPORTANT!:** The DATABASE user "ispconfig", have the `SELECT`, `INSERT`, `UPDATE` and `DELETE` privileges only, but this plugin requires the `ALTER` privilege. 41 | 42 | **Before** you install this plugin, you must log in to your **phpMyAdmin** (easier) and give the `ALTER` privilege to the "ispconfig" user in the "dbispconfig" database. 43 | 44 | _Notes:_ 45 | 46 | - _After finishing the installation of the plugin you can disable the `ALTER` privilege._ 47 | - _"dbispconfig" and "ispconfig" are the default database and database user for **ISPConfig**. You may have another one if you changed it during installation._ 48 | 49 | Login to your server and clone this repository: 50 | 51 | ```bash 52 | cd /tmp 53 | git clone https://github.com/mediabox-cl/ispconfig-nextcloud-plugin.git 54 | cd ispconfig-nextcloud-plugin 55 | cp -R interface /usr/local/ispconfig 56 | cp -R server /usr/local/ispconfig 57 | chown -R ispconfig:ispconfig /usr/local/ispconfig/interface/lib 58 | cd /usr/local/ispconfig/server/plugins-enabled 59 | ln -s /usr/local/ispconfig/server/plugins-available/nextcloud_plugin.inc.php nextcloud_plugin.inc.php 60 | rm -rf /tmp/ispconfig-nextcloud-plugin 61 | ``` 62 | 63 | Logout from your **ISPConfig 3** Control Panel and login again. **You must do this!** 64 | Now you can navigate to the **Nextcloud** tabs in the Server, Domain and Mailbox and make your configurations. 65 | 66 | ## Update 67 | 68 | ### Manual Update 69 | 70 | **Before** update, you must log in to your **phpMyAdmin** (easier) and give the `ALTER` privilege to the "ispconfig" user in the "dbispconfig" database. 71 | 72 | _Notes:_ 73 | 74 | - _After finishing the update you can disable the `ALTER` privilege._ 75 | - _"dbispconfig" and "ispconfig" are the default database and database user for **ISPConfig**. You may have another one if you changed it during installation._ 76 | 77 | Login to your server and clone this repository: 78 | 79 | ```bash 80 | cd /tmp 81 | git clone https://github.com/mediabox-cl/ispconfig-nextcloud-plugin.git 82 | cd ispconfig-nextcloud-plugin 83 | cp -R interface /usr/local/ispconfig 84 | cp -R server /usr/local/ispconfig 85 | chown -R ispconfig:ispconfig /usr/local/ispconfig/interface/lib 86 | rm -rf /tmp/ispconfig-nextcloud-plugin 87 | ``` 88 | 89 | Logout from your ISPConfig 3 Control Panel and login again. You must do this! 90 | 91 | ## Configuration 92 | 93 | ### Nextcloud API 94 | 95 | This plugin uses the **Nextcloud API** to delete users on **Nextcloud** who were deleted in **ISPConfig**. 96 | 97 | - Login to your **Nextcloud** installation with your admin account. 98 | - Navigate to `Personal settings > Security (Devices & sessions)` and create a new `APP Password`. 99 | - Input the APP name, for example: `ispconfig` and hit the `Create new app password` button. 100 | - Copy and paste the supplied credentials in `ISPConfig > Server > Nextcloud (TAB)`. 101 | 102 | Now, whenever you delete a user or domain in **ISPConfig**, this user or domain users will be deleted in **Nextcloud** (if enabled). 103 | 104 | ### What's next? 105 | 106 | Please, follow the instruction here to install the [Nextcloud - User ISPConfig API](https://github.com/mediabox-cl/nextcloud-user-ispconfig-api.git) APP. 107 | 108 | ## Thanks to: 109 | 110 | - Till Brehm from Projektfarm GmbH. 111 | - Falko Timme from Timme Hosting. 112 | - The ISPConfig community and developers. 113 | - The Nextcloud community and developers. 114 | -------------------------------------------------------------------------------- /interface/lib/classes/validate_nextcloud.inc.php: -------------------------------------------------------------------------------- 1 | tform->lng($errmsg) . $text . '
'; 42 | } 43 | 44 | /** 45 | * Check url 46 | * 47 | * @param $field_name 48 | * @param $field_value 49 | * @param $validator 50 | * 51 | * @return false|string 52 | */ 53 | function check_url($field_name, $field_value, $validator) 54 | { 55 | if ($field_value !== '') { 56 | if (filter_var($field_value, FILTER_VALIDATE_URL) === false) { 57 | return $this->get_error($validator['errmsg'], $field_value); 58 | } 59 | } 60 | 61 | return false; 62 | } 63 | } -------------------------------------------------------------------------------- /interface/lib/plugins/nextcloud_plugin.inc.php: -------------------------------------------------------------------------------- 1 | [ 40 | 'nc_enabled', 41 | 'nc_quota', 42 | 'nc_group' 43 | ], 44 | 'mail_user' => [ 45 | 'nc_enabled', 46 | 'nc_quota', 47 | 'nc_group', 48 | 'nc_adm_user', 49 | 'nc_server', 50 | 'nc_adm_server', 51 | 'nc_domain', 52 | 'nc_adm_domain' 53 | ] 54 | ]; 55 | 56 | public function __construct() 57 | { 58 | $this->plugin_dir = ISPC_ROOT_PATH . '/lib/plugins/' . $this->plugin_name; 59 | } 60 | 61 | /** 62 | * This function is called when the plugin is loaded 63 | * 64 | * @return void 65 | */ 66 | public function onLoad(): void 67 | { 68 | global $app; 69 | 70 | // Check if needed tables/columns exist 71 | $this->checkTables(); 72 | 73 | // Register for the events 74 | 75 | // We need this in the Domain tab to restore missing vars needed by ISPConfig, 76 | // also we set the "active" var to an empty string in order to bypass the dkim 77 | // check part. 78 | $app->plugin->registerEvent('mail:mail_domain:on_before_insert', $this->plugin_name, 'mail_domain_edit'); 79 | $app->plugin->registerEvent('mail:mail_domain:on_before_update', $this->plugin_name, 'mail_domain_edit'); 80 | 81 | // Server - Insert tabs || fields (Remote) 82 | $app->plugin->registerEvent('admin:server_config:on_after_formdef', $this->plugin_name, 'server_config_form'); 83 | $app->plugin->registerEvent('admin:server_config:on_remote_after_formdef', $this->plugin_name, 'server_config_form'); 84 | 85 | // Domain - Insert tabs || fields (Remote) 86 | $app->plugin->registerEvent('mail:mail_domain:on_after_formdef', $this->plugin_name, 'mail_domain_form'); 87 | $app->plugin->registerEvent('mail:mail_domain:on_remote_after_formdef', $this->plugin_name, 'mail_domain_form'); 88 | 89 | // Mailbox - Insert tabs || fields (Remote) 90 | $app->plugin->registerEvent('mail:mail_user:on_after_formdef', $this->plugin_name, 'mail_user_form'); 91 | $app->plugin->registerEvent('mail:mail_user:on_remote_after_formdef', $this->plugin_name, 'mail_user_form'); 92 | } 93 | 94 | private function checkTables(): void 95 | { 96 | global $app; 97 | 98 | foreach ($this->nc_tables as $table => $columns) { 99 | // Check if table exist 100 | $list = "'" . implode("','", $columns) . "'"; 101 | $sql = "SHOW COLUMNS FROM $table WHERE Field IN($list)"; 102 | $result = $app->db->queryAllArray($sql); 103 | 104 | if (!empty($result)) { 105 | $diff = array_diff($columns, $result); 106 | if ($diff) { 107 | $this->createColumns($table); 108 | } 109 | } else { 110 | $this->createColumns($table); 111 | } 112 | } 113 | } 114 | 115 | private function createColumns($table): void 116 | { 117 | global $app; 118 | 119 | $file = $this->plugin_dir . "/sql/$table.sql"; 120 | 121 | if (is_file($file)) { 122 | $sql = preg_replace('/\s+/', ' ', file_get_contents($file)); 123 | if ($sql) { 124 | $app->db->query($sql); 125 | } 126 | } 127 | } 128 | 129 | public function mail_domain_edit($event_name, $page_form): void 130 | { 131 | global $app; 132 | 133 | // INFO: This is a HACK because the mail domain part in ISPConfig 134 | // isn't prepared to use TABS :( 135 | 136 | // We need to set this because from our tab to the domain tab it 137 | // need the domain ID from $_GET and this is a POST. 138 | if (isset($page_form->dataRecord['id'])) { 139 | if (!isset($_GET['id'])) { 140 | $_GET['id'] = $page_form->dataRecord['id']; 141 | } 142 | } 143 | 144 | // Restore this vars in the form 145 | if (isset($page_form->dataRecord)) { 146 | $app->tpl->setVar( 147 | 'server_value', 148 | ($page_form->dataRecord['server_id'] ?? ''), 149 | true 150 | ); 151 | $app->tpl->setVar( 152 | 'domain_value', 153 | ($page_form->dataRecord['domain'] ?? ''), 154 | true 155 | ); 156 | $app->tpl->setVar( 157 | 'policy_value', 158 | ($page_form->dataRecord['policy'] ?? ''), 159 | true 160 | ); 161 | $app->tpl->setVar( 162 | 'dkim_private_value', 163 | ($page_form->dataRecord['dkim_private'] ?? ''), 164 | true 165 | ); 166 | $app->tpl->setVar( 167 | 'dkim_public_value', 168 | ($page_form->dataRecord['dkim_public'] ?? ''), 169 | true 170 | ); 171 | $app->tpl->setVar( 172 | 'dkim_selector_value', 173 | ($page_form->dataRecord['dkim_selector'] ?? ''), 174 | true 175 | ); 176 | $app->tpl->setVar( 177 | 'dns_record_value', 178 | ($page_form->dataRecord['dns_record'] ?? ''), 179 | true 180 | ); 181 | } 182 | } 183 | 184 | public function server_config_form($event_name, $page_form): void 185 | { 186 | $this->loadLang($page_form); 187 | 188 | $tabs = array( 189 | 'nextcloud' => array( 190 | 'title' => 'Nextcloud', 191 | 'width' => 100, 192 | 'template' => $this->plugin_dir . '/templates/server_config_edit.htm', 193 | 'fields' => array( 194 | 'nc_enabled' => array( 195 | 'datatype' => 'VARCHAR', 196 | 'formtype' => 'CHECKBOX', 197 | 'default' => 'n', 198 | 'value' => array( 199 | 1 => 'y', 200 | 0 => 'n' 201 | ) 202 | ), 203 | 'nc_account' => array( 204 | 'datatype' => 'VARCHAR', 205 | 'formtype' => 'CHECKBOX', 206 | 'default' => 'n', 207 | 'value' => array( 208 | 1 => 'y', 209 | 0 => 'n' 210 | ) 211 | ), 212 | 'nc_url' => array( 213 | 'datatype' => 'VARCHAR', 214 | 'formtype' => 'TEXT', 215 | 'filters' => array( 216 | 0 => array( 217 | 'event' => 'SAVE', 218 | 'type' => 'TRIM' 219 | ) 220 | ), 221 | 'validators' => array( 222 | 0 => array( 223 | 'type' => 'CUSTOM', 224 | 'class' => 'validate_nextcloud', 225 | 'function' => 'check_url', 226 | 'errmsg' => 'nc_url_error_function' 227 | ) 228 | ), 229 | 'default' => '', 230 | 'value' => '', 231 | 'maxlength' => '255' 232 | ), 233 | 'nc_user' => array( 234 | 'datatype' => 'VARCHAR', 235 | 'formtype' => 'TEXT', 236 | 'filters' => array( 237 | 0 => array( 238 | 'event' => 'SAVE', 239 | 'type' => 'TRIM' 240 | ) 241 | ), 242 | 'default' => '', 243 | 'value' => '', 244 | 'maxlength' => '255' 245 | ), 246 | 'nc_password' => array( 247 | 'datatype' => 'VARCHAR', 248 | 'formtype' => 'TEXT', 249 | 'default' => '', 250 | 'value' => '', 251 | 'maxlength' => '255' 252 | ), 253 | 'nc_group' => array( 254 | 'datatype' => 'VARCHAR', 255 | 'formtype' => 'TEXT', 256 | 'filters' => array( 257 | 0 => array( 258 | 'event' => 'SAVE', 259 | 'type' => 'TRIM' 260 | ) 261 | ), 262 | 'default' => '', 263 | 'value' => '', 264 | 'maxlength' => '255' 265 | ), 266 | 'nc_add' => array( 267 | 'datatype' => 'VARCHAR', 268 | 'formtype' => 'CHECKBOX', 269 | 'default' => 'y', 270 | 'value' => array( 271 | 1 => 'y', 272 | 0 => 'n' 273 | ) 274 | ), 275 | 'nc_remove' => array( 276 | 'datatype' => 'VARCHAR', 277 | 'formtype' => 'CHECKBOX', 278 | 'default' => 'n', 279 | 'value' => array( 280 | 1 => 'y', 281 | 0 => 'n' 282 | ) 283 | ), 284 | 'nc_delete' => array( 285 | 'datatype' => 'VARCHAR', 286 | 'formtype' => 'CHECKBOX', 287 | 'default' => 'n', 288 | 'value' => array( 289 | 1 => 'y', 290 | 0 => 'n' 291 | ) 292 | ), 293 | ) 294 | ) 295 | ); 296 | 297 | $this->insert($tabs, $page_form); 298 | } 299 | 300 | public function mail_domain_form($event_name, $page_form): void 301 | { 302 | $this->loadLang($page_form); 303 | 304 | $tabs = array( 305 | 'nextcloud' => array( 306 | 'title' => 'Nextcloud', 307 | 'width' => 100, 308 | 'template' => $this->plugin_dir . '/templates/mail_domain_edit.htm', 309 | 'fields' => array( 310 | 'nc_enabled' => array( 311 | 'datatype' => 'VARCHAR', 312 | 'formtype' => 'CHECKBOX', 313 | 'default' => 'n', 314 | 'value' => array( 315 | 1 => 'y', 316 | 0 => 'n' 317 | ) 318 | ), 319 | 'nc_quota' => array( 320 | 'datatype' => 'VARCHAR', 321 | 'formtype' => 'TEXT', 322 | 'validators' => array( 323 | 0 => array( 324 | 'type' => 'REGEX', 325 | 'regex' => '/^([0-9]+)$/', 326 | 'errmsg' => 'nc_quota_error_regex' 327 | ), 328 | ), 329 | 'default' => '0', 330 | 'value' => '', 331 | 'maxlength' => '255' 332 | ), 333 | 'nc_group' => array( 334 | 'datatype' => 'VARCHAR', 335 | 'formtype' => 'TEXT', 336 | 'filters' => array( 337 | 0 => array( 338 | 'event' => 'SAVE', 339 | 'type' => 'TRIM' 340 | ) 341 | ), 342 | 'default' => '', 343 | 'value' => '', 344 | 'maxlength' => '255' 345 | ), 346 | ) 347 | ) 348 | ); 349 | 350 | $this->insert($tabs, $page_form); 351 | } 352 | 353 | public function mail_user_form($event_name, $page_form): void 354 | { 355 | $this->loadLang($page_form); 356 | 357 | $tabs = array( 358 | 'nextcloud' => array( 359 | 'title' => 'Nextcloud', 360 | 'width' => 100, 361 | 'template' => $this->plugin_dir . '/templates/mail_user_edit.htm', 362 | 'fields' => array( 363 | 'nc_enabled' => array( 364 | 'datatype' => 'VARCHAR', 365 | 'formtype' => 'CHECKBOX', 366 | 'default' => 'y', 367 | 'value' => array( 368 | 1 => 'y', 369 | 0 => 'n' 370 | ) 371 | ), 372 | 'nc_quota' => array( 373 | 'datatype' => 'VARCHAR', 374 | 'formtype' => 'TEXT', 375 | 'validators' => array( 376 | 0 => array( 377 | 'type' => 'REGEX', 378 | 'regex' => '/^([0-9]*)$/', 379 | 'errmsg' => 'nc_quota_user_error_regex' 380 | ), 381 | ), 382 | 'default' => '', 383 | 'value' => '', 384 | 'maxlength' => '255' 385 | ), 386 | 'nc_group' => array( 387 | 'datatype' => 'VARCHAR', 388 | 'formtype' => 'TEXT', 389 | 'filters' => array( 390 | 0 => array( 391 | 'event' => 'SAVE', 392 | 'type' => 'TRIM' 393 | ) 394 | ), 395 | 'default' => '', 396 | 'value' => '', 397 | 'maxlength' => '255' 398 | ), 399 | 'nc_adm_user' => array( 400 | 'datatype' => 'VARCHAR', 401 | 'formtype' => 'CHECKBOX', 402 | 'default' => 'n', 403 | 'value' => array( 404 | 1 => 'y', 405 | 0 => 'n' 406 | ) 407 | ), 408 | 'nc_server' => array( 409 | 'datatype' => 'VARCHAR', 410 | 'formtype' => 'CHECKBOX', 411 | 'default' => 'y', 412 | 'value' => array( 413 | 1 => 'y', 414 | 0 => 'n' 415 | ) 416 | ), 417 | 'nc_adm_server' => array( 418 | 'datatype' => 'VARCHAR', 419 | 'formtype' => 'CHECKBOX', 420 | 'default' => 'n', 421 | 'value' => array( 422 | 1 => 'y', 423 | 0 => 'n' 424 | ) 425 | ), 426 | 'nc_domain' => array( 427 | 'datatype' => 'VARCHAR', 428 | 'formtype' => 'CHECKBOX', 429 | 'default' => 'y', 430 | 'value' => array( 431 | 1 => 'y', 432 | 0 => 'n' 433 | ) 434 | ), 435 | 'nc_adm_domain' => array( 436 | 'datatype' => 'VARCHAR', 437 | 'formtype' => 'CHECKBOX', 438 | 'default' => 'n', 439 | 'value' => array( 440 | 1 => 'y', 441 | 0 => 'n' 442 | ) 443 | ), 444 | ) 445 | ) 446 | ); 447 | 448 | $this->insert($tabs, $page_form); 449 | } 450 | 451 | private function loadLang($page_form): void 452 | { 453 | global $app, $conf; 454 | 455 | $language = $app->functions->check_language( 456 | $_SESSION['s']['user']['language'] ?? $conf['language'] 457 | ); 458 | 459 | $file = $this->plugin_dir . "/lib/lang/$language.lng"; 460 | 461 | if (!is_file($file)) { 462 | $file = $this->plugin_dir . "/lib/lang/en.lng"; 463 | } 464 | 465 | @include $file; 466 | 467 | if (isset($page_form->wordbook) && isset($wb) && is_array($wb)) { 468 | 469 | if (is_array($page_form->wordbook)) { 470 | $page_form->wordbook = array_merge($page_form->wordbook, $wb); 471 | } else { 472 | $page_form->wordbook = $wb; 473 | } 474 | } 475 | } 476 | 477 | private function insert($tabs, $page_form): void 478 | { 479 | if (isset($page_form->formDef['tabs'])) { 480 | $page_form->formDef['tabs'] += $tabs; 481 | } elseif (isset($page_form->formDef['fields'])) { 482 | foreach ($tabs as $tab) { 483 | foreach ($tab['fields'] as $key => $value) { 484 | $page_form->formDef['fields'][$key] = $value; 485 | } 486 | } 487 | } 488 | } 489 | } -------------------------------------------------------------------------------- /interface/lib/plugins/nextcloud_plugin/VERSION: -------------------------------------------------------------------------------- 1 | 29.0.1 -------------------------------------------------------------------------------- /interface/lib/plugins/nextcloud_plugin/lib/lang/en.lng: -------------------------------------------------------------------------------- 1 | 1'; 55 | $wb['nc_group_domain_txt'] = 'Domain Group'; 56 | $wb['nc_group_domain_desc_txt'] = 'Domain group for users with mailboxes in this domain. If leave empty, no group will be created.'; 57 | $wb['nc_quota_user_txt'] = 'User Quota'; 58 | $wb['nc_quota_user_desc_txt'] = 'Leave empty to use the default domain quota.'; 59 | $wb['nc_quota_user_error_regex'] = 'Invalid quota value. Allowed values are: Empty to use the domain quota, 0 for none or numbers > 1'; 60 | $wb['nc_group_user_txt'] = 'User Group'; 61 | $wb['nc_group_user_desc_txt'] = 'Specific user group. If leave empty, no group will be created.'; 62 | $wb['nc_group_adm_user_txt'] = 'User Group Admin'; 63 | $wb['nc_group_adm_user_desc_txt'] = 'Makes the user an admin of their own user group.'; 64 | $wb['nc_group_adm_server_txt'] = 'Server Group Admin'; 65 | $wb['nc_group_adm_server_desc_txt'] = 'Makes the user an admin of their own server group'; 66 | $wb['nc_group_adm_domain_txt'] = 'Domain Group Admin'; 67 | $wb['nc_group_adm_domain_desc_txt'] = 'Makes the user an admin of their own domain group'; 68 | $wb['nc_group_user_server_desc_txt'] = 'Add this user to the Server group.'; 69 | $wb['nc_group_user_domain_desc_txt'] = 'Add this user to the Domain group.'; 70 | -------------------------------------------------------------------------------- /interface/lib/plugins/nextcloud_plugin/screenshots/ispconfig-01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mediabox-cl/ispconfig-nextcloud-plugin/6e031c1ad3cd5a79279436fbb1c13b953d6b219b/interface/lib/plugins/nextcloud_plugin/screenshots/ispconfig-01.png -------------------------------------------------------------------------------- /interface/lib/plugins/nextcloud_plugin/screenshots/ispconfig-02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mediabox-cl/ispconfig-nextcloud-plugin/6e031c1ad3cd5a79279436fbb1c13b953d6b219b/interface/lib/plugins/nextcloud_plugin/screenshots/ispconfig-02.png -------------------------------------------------------------------------------- /interface/lib/plugins/nextcloud_plugin/screenshots/ispconfig-03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mediabox-cl/ispconfig-nextcloud-plugin/6e031c1ad3cd5a79279436fbb1c13b953d6b219b/interface/lib/plugins/nextcloud_plugin/screenshots/ispconfig-03.png -------------------------------------------------------------------------------- /interface/lib/plugins/nextcloud_plugin/sql/mail_domain.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE mail_domain 2 | ADD COLUMN IF NOT EXISTS nc_enabled enum ('n','y') NOT NULL DEFAULT 'n', 3 | ADD COLUMN IF NOT EXISTS nc_quota varchar(255) NOT NULL DEFAULT '0', 4 | ADD COLUMN IF NOT EXISTS nc_group varchar(255) NOT NULL DEFAULT ''; -------------------------------------------------------------------------------- /interface/lib/plugins/nextcloud_plugin/sql/mail_user.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE mail_user 2 | ADD COLUMN IF NOT EXISTS nc_enabled enum ('n','y') NOT NULL DEFAULT 'y', 3 | ADD COLUMN IF NOT EXISTS nc_quota varchar(255) NOT NULL DEFAULT '', 4 | ADD COLUMN IF NOT EXISTS nc_group varchar(255) NOT NULL DEFAULT '', 5 | ADD COLUMN IF NOT EXISTS nc_adm_user enum ('n','y') NOT NULL DEFAULT 'n', 6 | ADD COLUMN IF NOT EXISTS nc_server enum ('n','y') NOT NULL DEFAULT 'y', 7 | ADD COLUMN IF NOT EXISTS nc_adm_server enum ('n','y') NOT NULL DEFAULT 'n', 8 | ADD COLUMN IF NOT EXISTS nc_domain enum ('n','y') NOT NULL DEFAULT 'y', 9 | ADD COLUMN IF NOT EXISTS nc_adm_domain enum ('n','y') NOT NULL DEFAULT 'n'; -------------------------------------------------------------------------------- /interface/lib/plugins/nextcloud_plugin/templates/mail_domain_edit.htm: -------------------------------------------------------------------------------- 1 |
2 | 5 |
6 | {tmpl_var name='nc_enabled'} 7 |
8 |
9 | 10 |
11 | 14 |
15 |
16 | 18 | MB 19 |
20 |
21 | {tmpl_var name='nc_quota_domain_desc_txt'} 22 |
23 |
24 |
25 | 26 |
27 | 30 |
31 | 33 |
34 | {tmpl_var name='nc_group_domain_desc_txt'} 35 |
36 |
37 |
38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 |
65 | 66 | 67 | 69 | 70 | 71 | 72 | 73 |
74 | 78 | 82 |
83 |
84 | -------------------------------------------------------------------------------- /interface/lib/plugins/nextcloud_plugin/templates/mail_user_edit.htm: -------------------------------------------------------------------------------- 1 |
2 | 5 |
6 | {tmpl_var name='nc_enabled'} 7 |
8 |
9 | 10 |
11 | 14 |
15 |
16 | 18 | MB 19 |
20 |
21 | {tmpl_var name='nc_quota_user_desc_txt'} 22 |
23 |
24 |
25 | 26 |
27 | 30 |
31 | 33 |
34 | {tmpl_var name='nc_group_user_desc_txt'} 35 |
36 |
37 |
38 | 39 |
40 | 43 |
44 | {tmpl_var name='nc_adm_user'} 45 | 46 | {tmpl_var name='nc_group_adm_user_desc_txt'} 47 | 48 |
49 |
50 | 51 |
52 | 55 |
56 | {tmpl_var name='nc_server'} 57 | 58 | {tmpl_var name='nc_group_user_server_desc_txt'} 59 | 60 |
61 |
62 | 63 |
64 | 67 |
68 | {tmpl_var name='nc_adm_server'} 69 | 70 | {tmpl_var name='nc_group_adm_server_desc_txt'} 71 | 72 |
73 |
74 | 75 |
76 | 79 |
80 | {tmpl_var name='nc_domain'} 81 | 82 | {tmpl_var name='nc_group_user_domain_desc_txt'} 83 | 84 |
85 |
86 | 87 |
88 | 91 |
92 | {tmpl_var name='nc_adm_domain'} 93 | 94 | {tmpl_var name='nc_group_adm_domain_desc_txt'} 95 | 96 |
97 |
98 | 99 | 100 | 101 |
102 | 103 | 104 | 106 | 107 | 108 | 109 | 110 |
111 | 115 | 119 |
120 |
121 | -------------------------------------------------------------------------------- /interface/lib/plugins/nextcloud_plugin/templates/server_config_edit.htm: -------------------------------------------------------------------------------- 1 |
2 |

3 | {tmpl_var name='server_name'} 4 |

5 |
6 | 7 |
8 | 11 |
12 | {tmpl_var name='nc_enabled'} 13 |
14 |
15 | 16 |
17 | 20 |
21 | {tmpl_var name='nc_account'} 22 | 23 | {tmpl_var name='nc_account_desc_txt'} 24 | 25 |
26 |
27 | 28 |
29 | 30 |
31 | 33 |
34 | {tmpl_var name='nc_url_desc_txt'} https://cloud.domain.tld 35 |
36 |
37 |
38 | 39 |
40 | 41 |
42 | 44 |
45 | {tmpl_var name='nc_user_desc_txt'} 46 |
47 |
48 |
49 | 50 |
51 | 54 |
55 | 57 |
58 | {tmpl_var name='nc_password_desc_txt'} 59 |
60 |
61 |
62 | 63 |
64 | 67 |
68 | 70 |
71 | {tmpl_var name='nc_group_server_desc_txt'} 72 |
73 |
74 |
75 | 76 |
77 | 80 |
81 | {tmpl_var name='nc_add'} 82 | 83 | {tmpl_var name='nc_add_desc_txt'} 84 | 85 |
86 |
87 | 88 |
89 | 92 |
93 | {tmpl_var name='nc_remove'} 94 | 95 | {tmpl_var name='nc_remove_desc_txt'} 96 | 97 |
98 | {tmpl_var name='nc_remove_note_desc_txt'} 99 |
100 |
101 |
102 | 103 |
104 | 107 |
108 | {tmpl_var name='nc_delete'} 109 | 110 | {tmpl_var name='nc_delete_desc_txt'} 111 | 112 |
113 | {tmpl_var name='nc_delete_note_desc_txt'} 114 |
115 |
116 |
117 | 118 | 119 | 120 |
121 |
122 | 126 | 130 |
131 |
132 | -------------------------------------------------------------------------------- /server/plugins-available/nextcloud_plugin.inc.php: -------------------------------------------------------------------------------- 1 | [ 43 | 'method' => 'GET', 44 | 'path' => 'ocs/v1.php/cloud/users/{uid}', 45 | 'header' => ['OCS-APIRequest: true'], 46 | 'success' => 100, 47 | ], 48 | 'delete' => [ 49 | 'method' => 'DELETE', 50 | 'path' => 'ocs/v1.php/cloud/users/{uid}', 51 | 'header' => ['OCS-APIRequest: true'], 52 | 'success' => 100, 53 | ] 54 | ]; 55 | 56 | // Plugin 57 | public string $plugin_name = 'nextcloud_plugin'; 58 | public string $class_name = 'nextcloud_plugin'; 59 | 60 | // Private variables 61 | public string $action = ''; 62 | 63 | // Nextcloud 64 | private ?bool $nc_enabled = null; 65 | private string $nc_url; 66 | private string $nc_path; 67 | private string $nc_user; 68 | private string $nc_password; 69 | 70 | /** 71 | * This function is called during ISPConfig installation to determine 72 | * if a symlink shall be created for this plugin. 73 | */ 74 | function onInstall(): bool 75 | { 76 | global $conf; 77 | 78 | return (bool) $conf['services']['mail']; 79 | } 80 | 81 | /** 82 | * This function is called when the plugin is loaded 83 | */ 84 | public function onLoad(): void 85 | { 86 | global $app; 87 | 88 | // Register for the events 89 | 90 | // Mailboxes 91 | $app->plugins->registerEvent('mail_user_delete', $this->plugin_name, 'mail_user_delete'); 92 | 93 | // Mail Domains 94 | $app->plugins->registerEvent('mail_domain_delete', $this->plugin_name, 'mail_domain_delete'); 95 | } 96 | 97 | /** 98 | * Mail user delete event 99 | * 100 | * @param $event_name 101 | * @param $data 102 | * 103 | * @return void 104 | */ 105 | public function mail_user_delete($event_name, $data): void 106 | { 107 | $this->loadConf(); 108 | 109 | if ($this->nc_enabled) { 110 | $this->user_delete($data['old']['email']); 111 | } 112 | } 113 | 114 | /** 115 | * Mail domain delete event 116 | * 117 | * @param $event_name 118 | * @param $data 119 | * 120 | * @return void 121 | */ 122 | public function mail_domain_delete($event_name, $data): void 123 | { 124 | global $app; 125 | 126 | $this->loadConf(); 127 | 128 | if ($this->nc_enabled) { 129 | $mail_users = $app->db->queryAllRecords("SELECT email FROM mail_user WHERE email like ?", '%@' . $data['old']['domain']); 130 | 131 | if (is_array($mail_users)) { 132 | foreach ($mail_users as $user) { 133 | $this->user_delete($user['email']); 134 | } 135 | } 136 | } 137 | } 138 | 139 | /** 140 | * Delete user 141 | * 142 | * @param string $email 143 | * 144 | * @return void 145 | */ 146 | private function user_delete(string $email): void 147 | { 148 | global $app; 149 | 150 | $uid = $this->getUid($email); 151 | 152 | $this->nc_path = str_replace('{uid}', $uid, self::PATHS['delete']['path']); 153 | $data = $this->call(self::PATHS['delete']['method'], self::PATHS['delete']['header']); 154 | 155 | if (!$data['error'] && $data['response']) { 156 | $status = $this->getStatus($data['response']); 157 | 158 | if ($status === self::PATHS['delete']['success']) { 159 | $app->log("Deleted the Nextcloud account for User: $uid", LOGLEVEL_WARN); 160 | } else { 161 | $app->log("Can't deleted the Nextcloud account for User: $uid - Status code: $status", LOGLEVEL_ERROR); 162 | } 163 | } else { 164 | $app->log("Can't deleted the Nextcloud account for User: $uid - Error: " . $data['description'], LOGLEVEL_ERROR); 165 | } 166 | } 167 | 168 | /** 169 | * Get status code from xml response 170 | * 171 | * @param string $xml 172 | * 173 | * @return int 174 | */ 175 | private function getStatus(string $xml): int 176 | { 177 | if (preg_match('/(\d+)<\/statuscode>/', $xml, $matches)) { 178 | return (int) $matches[1]; 179 | } 180 | 181 | return 0; 182 | } 183 | 184 | /** 185 | * Compute UID from email 186 | * 187 | * @param string $email 188 | * 189 | * @return string 190 | */ 191 | private function getUid(string $email): string 192 | { 193 | $parts = explode('@', mb_strtolower(trim($email))); 194 | $mailDomain = array_pop($parts); 195 | $mailUser = implode('@', $parts); 196 | 197 | $uid = "$mailUser.$mailDomain"; 198 | 199 | if (mb_strlen($uid) > self::MAX_UID_LENGTH) { 200 | $uid = hash('sha256', $uid); 201 | } 202 | 203 | return $uid; 204 | } 205 | 206 | /** 207 | * Curl call 208 | * 209 | * @param string $method 210 | * @param array $headers 211 | * 212 | * @return array 213 | */ 214 | private function call(string $method, array $headers): array 215 | { 216 | $url = rtrim($this->nc_url, '/') . '/' . $this->nc_path; 217 | $ch = curl_init($url); 218 | 219 | curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); 220 | curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); 221 | curl_setopt($ch, CURLINFO_HEADER_OUT, true); 222 | curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); 223 | curl_setopt($ch, CURLOPT_MAXREDIRS, 10); 224 | curl_setopt($ch, CURLOPT_TIMEOUT, 30); 225 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); 226 | curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC); 227 | curl_setopt($ch, CURLOPT_USERPWD, $this->nc_user . ':' . $this->nc_password); 228 | curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); 229 | curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method); 230 | 231 | $response = curl_exec($ch); 232 | $error = curl_errno($ch); 233 | $http = curl_getinfo($ch, CURLINFO_HTTP_CODE); 234 | 235 | curl_close($ch); 236 | 237 | return [ 238 | 'http' => $http, 239 | 'error' => $error, 240 | 'description' => ($error ? curl_strerror($error) : ''), 241 | 'response' => $response 242 | ]; 243 | } 244 | 245 | /** 246 | * Load config 247 | * 248 | * @return void 249 | */ 250 | private function loadConf(): void 251 | { 252 | global $app, $conf; 253 | 254 | if ($this->nc_enabled === null) { 255 | // load the server specific configuration options for nextcloud 256 | $app->uses('getconf'); 257 | $nc_config = $app->getconf->get_server_config($conf['server_id'], 'nextcloud'); 258 | $this->nc_enabled = is_array($nc_config) && isset($nc_config['nc_enabled']) && $nc_config['nc_enabled'] == 'y'; 259 | 260 | if ( 261 | $this->nc_enabled && 262 | isset($nc_config['nc_account']) && 263 | $nc_config['nc_account'] == 'y' && 264 | isset($nc_config['nc_url']) && 265 | $nc_config['nc_url'] && 266 | isset($nc_config['nc_user']) && 267 | $nc_config['nc_user'] && 268 | isset($nc_config['nc_password']) && 269 | $nc_config['nc_password'] 270 | ) { 271 | $this->nc_url = $nc_config['nc_url']; 272 | $this->nc_user = $nc_config['nc_user']; 273 | $this->nc_password = $nc_config['nc_password']; 274 | } 275 | } 276 | } 277 | } --------------------------------------------------------------------------------