diff --git a/CHANGELOG.md b/CHANGELOG.md
index c571216..90714c8 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,7 @@ v2.1
----
- Fix FLEXIAPI-282 Migrate to Laravel 11 and PHP 8.2+
- Fix FLEXIAPI-371 Add documentation for the Wizard page
+- Fix FLEXIAPI-359 Add CardDav servers support in the spaces
v2.0
----
diff --git a/flexiapi/app/Account.php b/flexiapi/app/Account.php
index 3427ca8..e63c5c9 100644
--- a/flexiapi/app/Account.php
+++ b/flexiapi/app/Account.php
@@ -25,6 +25,7 @@ use Illuminate\Support\Facades\Auth;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Http\Request;
use Illuminate\Support\Str;
+use Illuminate\Database\Eloquent\Collection;
use Carbon\Carbon;
use Awobaz\Compoships\Compoships;
@@ -36,7 +37,7 @@ class Account extends Authenticatable
use HasFactory;
use Compoships;
- protected $with = ['passwords', 'emailChangeCode', 'types', 'actions', 'dictionaryEntries'];
+ protected $with = ['passwords', 'emailChangeCode', 'types', 'actions', 'dictionaryEntries', 'carddavServers'];
protected $hidden = ['expire_time', 'pivot', 'currentProvisioningToken', 'currentRecoveryCode', 'dictionaryEntries'];
protected $appends = ['realm', 'provisioning_token', 'provisioning_token_expire_at', 'dictionary'];
protected $casts = [
@@ -149,6 +150,12 @@ class Account extends Authenticatable
return $this->hasMany(AccountDictionaryEntry::class);
}
+ public function carddavServers()
+ {
+ return $this->belongsToMany(SpaceCardDavServer::class, 'account_carddav_credentials', 'account_id', 'space_carddav_server_id')
+ ->withPivot('username', 'domain', 'algorithm', 'password');
+ }
+
public function getDictionaryAttribute()
{
if ($this->dictionaryEntries->isEmpty()) return new stdClass;
@@ -309,6 +316,15 @@ class Account extends Authenticatable
return null;
}
+ public function getRemainingCardDavCredentialsCreatableAttribute(): Collection
+ {
+ return $this->space->carddavServers()->whereNotIn('id', function ($query) {
+ $query->select('space_carddav_server_id')
+ ->from('account_carddav_credentials')
+ ->where('account_id', $this->id);
+ })->get();
+ }
+
public function getIdentifierAttribute(): string
{
return $this->attributes['username'] . '@' . $this->attributes['domain'];
diff --git a/flexiapi/app/AccountCardDavCredentials.php b/flexiapi/app/AccountCardDavCredentials.php
new file mode 100644
index 0000000..92846fc
--- /dev/null
+++ b/flexiapi/app/AccountCardDavCredentials.php
@@ -0,0 +1,20 @@
+hasOne(SpaceCardDavServer::class, 'id', 'space_carddav_server_id');
+ }
+
+ public function getIdentifierAttribute()
+ {
+ return $this->username . '@' . $this->domain;
+ }
+}
diff --git a/flexiapi/app/Helpers/Utils.php b/flexiapi/app/Helpers/Utils.php
index 443f81b..f61d0a8 100644
--- a/flexiapi/app/Helpers/Utils.php
+++ b/flexiapi/app/Helpers/Utils.php
@@ -28,11 +28,9 @@ use League\CommonMark\Extension\HeadingPermalink\HeadingPermalinkExtension;
use League\CommonMark\Extension\TableOfContents\TableOfContentsExtension;
use Illuminate\Support\Facades\DB;
-$hostSpace = null;
-
function space(): ?Space
{
- return request()->space;
+ return is_object(request()->space) ? request()->space :null;
}
function passwordAlgorithms(): array
diff --git a/flexiapi/app/Http/Controllers/Account/ProvisioningController.php b/flexiapi/app/Http/Controllers/Account/ProvisioningController.php
index 059dd46..b1cd4c3 100644
--- a/flexiapi/app/Http/Controllers/Account/ProvisioningController.php
+++ b/flexiapi/app/Http/Controllers/Account/ProvisioningController.php
@@ -189,6 +189,44 @@ class ProvisioningController extends Controller
}
}
+ $remoteContactDirectoryCounter = 0;
+ $authInfoIndex = 0;
+
+ // CardDav servers
+
+ if ($request->space?->carddavServers) {
+ foreach ($request->space->carddavServers as $carddavServer) {
+ $carddavServer->getProvisioningSection($config, $remoteContactDirectoryCounter);
+ $remoteContactDirectoryCounter++;
+ }
+ }
+
+ if ($account) {
+ foreach ($account->carddavServers as $carddavServer) {
+ $section = $dom->createElement('section');
+ $section->setAttribute('name', 'auth_info_' . $authInfoIndex);
+ $config->appendChild($section);
+
+ $entry = $dom->createElement('entry', $carddavServer->pivot->username);
+ $entry->setAttribute('name', 'username');
+ $section->appendChild($entry);
+
+ $entry = $dom->createElement('entry', $carddavServer->pivot->domain);
+ $entry->setAttribute('name', 'domain');
+ $section->appendChild($entry);
+
+ $entry = $dom->createElement('entry', $carddavServer->pivot->password);
+ $entry->setAttribute('name', 'ha1');
+ $section->appendChild($entry);
+
+ $entry = $dom->createElement('entry', $carddavServer->pivot->algorithm);
+ $entry->setAttribute('name', 'algorithm');
+ $section->appendChild($entry);
+
+ $authInfoIndex++;
+ }
+ }
+
// Password reset
if ($account && $request->has('reset_password')) {
$account->updatePassword(Str::random(10));
diff --git a/flexiapi/app/Http/Controllers/Admin/AccountAccountTypeController.php b/flexiapi/app/Http/Controllers/Admin/Account/AccountTypeController.php
similarity index 96%
rename from flexiapi/app/Http/Controllers/Admin/AccountAccountTypeController.php
rename to flexiapi/app/Http/Controllers/Admin/Account/AccountTypeController.php
index 110569e..817568b 100644
--- a/flexiapi/app/Http/Controllers/Admin/AccountAccountTypeController.php
+++ b/flexiapi/app/Http/Controllers/Admin/Account/AccountTypeController.php
@@ -17,7 +17,7 @@
along with this program. If not, see .
*/
-namespace App\Http\Controllers\Admin;
+namespace App\Http\Controllers\Admin\Account;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
@@ -26,7 +26,7 @@ use Illuminate\Support\Facades\Log;
use App\Account;
use App\AccountType;
-class AccountAccountTypeController extends Controller
+class AccountTypeController extends Controller
{
public function create(int $id)
{
diff --git a/flexiapi/app/Http/Controllers/Admin/AccountActionController.php b/flexiapi/app/Http/Controllers/Admin/Account/ActionController.php
similarity index 97%
rename from flexiapi/app/Http/Controllers/Admin/AccountActionController.php
rename to flexiapi/app/Http/Controllers/Admin/Account/ActionController.php
index c3d1d1f..0419600 100644
--- a/flexiapi/app/Http/Controllers/Admin/AccountActionController.php
+++ b/flexiapi/app/Http/Controllers/Admin/Account/ActionController.php
@@ -17,7 +17,7 @@
along with this program. If not, see .
*/
-namespace App\Http\Controllers\Admin;
+namespace App\Http\Controllers\Admin\Account;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
@@ -27,7 +27,7 @@ use App\Account;
use App\AccountAction;
use App\Rules\NoUppercase;
-class AccountActionController extends Controller
+class ActionController extends Controller
{
public function create(int $accountId)
{
diff --git a/flexiapi/app/Http/Controllers/Admin/AccountActivityController.php b/flexiapi/app/Http/Controllers/Admin/Account/ActivityController.php
similarity index 92%
rename from flexiapi/app/Http/Controllers/Admin/AccountActivityController.php
rename to flexiapi/app/Http/Controllers/Admin/Account/ActivityController.php
index e4ffefb..d3882fa 100644
--- a/flexiapi/app/Http/Controllers/Admin/AccountActivityController.php
+++ b/flexiapi/app/Http/Controllers/Admin/Account/ActivityController.php
@@ -17,13 +17,13 @@
along with this program. If not, see .
*/
-namespace App\Http\Controllers\Admin;
+namespace App\Http\Controllers\Admin\Account;
use App\Http\Controllers\Controller;
use App\Account;
-class AccountActivityController extends Controller
+class ActivityController extends Controller
{
public function index(int $accountId)
{
diff --git a/flexiapi/app/Http/Controllers/Admin/Account/CardDavCredentialsController.php b/flexiapi/app/Http/Controllers/Admin/Account/CardDavCredentialsController.php
new file mode 100644
index 0000000..a551c04
--- /dev/null
+++ b/flexiapi/app/Http/Controllers/Admin/Account/CardDavCredentialsController.php
@@ -0,0 +1,104 @@
+.
+*/
+
+namespace App\Http\Controllers\Admin\Account;
+
+use App\Account;
+use App\AccountCardDavCredentials;
+use App\Http\Controllers\Controller;
+use Illuminate\Database\Query\Builder;
+use Illuminate\Http\Request;
+use Illuminate\Validation\Rule;
+use App\Http\Requests\Account\CardDavCredentials;
+
+class CardDavCredentialsController extends Controller
+{
+ public function create(int $accountId)
+ {
+ $account = Account::findOrFail($accountId);
+ $this->checkFeatureEnabled($account);
+
+ return view('admin.account.carddav.create', [
+ 'account' => $account,
+ 'carddavServers' => $account->remainingCardDavCredentialsCreatable
+ ]);
+ }
+
+ public function store(CardDavCredentials $request, int $accountId)
+ {
+ $account = Account::findOrFail($accountId);
+ $this->checkFeatureEnabled($account);
+
+ $request->validate([
+ 'carddav_id' => ['required', Rule::exists('space_carddav_servers', 'id')->where(function (Builder $query) use ($account) {
+ return $query->where('space_id', $account->space->id);
+ })]
+ ]);
+
+ $accountCarddavCredentials = new AccountCardDavCredentials;
+ $accountCarddavCredentials->space_carddav_server_id = $request->get('carddav_id');
+ $accountCarddavCredentials->account_id = $account->id;
+ $accountCarddavCredentials->username = $request->get('username');
+ $accountCarddavCredentials->domain = $request->get('domain');
+ $accountCarddavCredentials->password = bchash(
+ $request->get('username'),
+ $request->get('domain'),
+ $request->get('password'),
+ $request->get('algorithm')
+ );
+ $accountCarddavCredentials->algorithm = $request->get('algorithm');
+ $accountCarddavCredentials->save();
+
+ return redirect()->route('admin.account.show', $account);
+ }
+
+ public function delete(int $accountId, int $cardDavId)
+ {
+ $account = Account::findOrFail($accountId);
+ $this->checkFeatureEnabled($account);
+
+ $accountCarddavCredentials = AccountCardDavCredentials::where('space_carddav_server_id', $cardDavId)
+ ->where('account_id', $account->id)
+ ->firstOrFail();
+
+ return view('admin.account.carddav.delete', [
+ 'account' => $account,
+ 'carddavCredentials' => $accountCarddavCredentials,
+ ]);
+ }
+
+ public function destroy(Request $request, int $accountId)
+ {
+ $account = Account::findOrFail($accountId);
+ $this->checkFeatureEnabled($account);
+
+ $accountCarddavCredentials = AccountCardDavCredentials::where('space_carddav_server_id', $request->carddav_id)
+ ->where('account_id', $account->id)
+ ->delete();
+
+ return redirect()->route('admin.account.show', $account);
+ }
+
+ private function checkFeatureEnabled(Account $account)
+ {
+ if (!$account->space->carddav_user_credentials) {
+ abort(403, 'CardDav Credentials features disabled');
+ }
+ }
+}
diff --git a/flexiapi/app/Http/Controllers/Admin/AccountContactController.php b/flexiapi/app/Http/Controllers/Admin/Account/ContactController.php
similarity index 97%
rename from flexiapi/app/Http/Controllers/Admin/AccountContactController.php
rename to flexiapi/app/Http/Controllers/Admin/Account/ContactController.php
index ff8c46e..0f07173 100644
--- a/flexiapi/app/Http/Controllers/Admin/AccountContactController.php
+++ b/flexiapi/app/Http/Controllers/Admin/Account/ContactController.php
@@ -17,7 +17,7 @@
along with this program. If not, see .
*/
-namespace App\Http\Controllers\Admin;
+namespace App\Http\Controllers\Admin\Account;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
@@ -26,7 +26,7 @@ use Illuminate\Support\Facades\Log;
use App\Account;
use App\ContactsList;
-class AccountContactController extends Controller
+class ContactController extends Controller
{
public function index(int $accountId)
{
diff --git a/flexiapi/app/Http/Controllers/Admin/AccountDeviceController.php b/flexiapi/app/Http/Controllers/Admin/Account/DeviceController.php
similarity index 95%
rename from flexiapi/app/Http/Controllers/Admin/AccountDeviceController.php
rename to flexiapi/app/Http/Controllers/Admin/Account/DeviceController.php
index df76ecf..80d4775 100644
--- a/flexiapi/app/Http/Controllers/Admin/AccountDeviceController.php
+++ b/flexiapi/app/Http/Controllers/Admin/Account/DeviceController.php
@@ -17,14 +17,14 @@
along with this program. If not, see .
*/
-namespace App\Http\Controllers\Admin;
+namespace App\Http\Controllers\Admin\Account;
use App\Account;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use App\Libraries\FlexisipRedisConnector;
-class AccountDeviceController extends Controller
+class DeviceController extends Controller
{
public function index(int $accountId)
{
diff --git a/flexiapi/app/Http/Controllers/Admin/AccountDictionaryController.php b/flexiapi/app/Http/Controllers/Admin/Account/DictionaryController.php
similarity index 97%
rename from flexiapi/app/Http/Controllers/Admin/AccountDictionaryController.php
rename to flexiapi/app/Http/Controllers/Admin/Account/DictionaryController.php
index 4118fcf..b32a1f3 100644
--- a/flexiapi/app/Http/Controllers/Admin/AccountDictionaryController.php
+++ b/flexiapi/app/Http/Controllers/Admin/Account/DictionaryController.php
@@ -17,7 +17,7 @@
along with this program. If not, see .
*/
-namespace App\Http\Controllers\Admin;
+namespace App\Http\Controllers\Admin\Account;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
@@ -25,7 +25,7 @@ use Illuminate\Http\Request;
use App\Account;
use App\AccountDictionaryEntry;
-class AccountDictionaryController extends Controller
+class DictionaryController extends Controller
{
public function create(int $accountId)
{
diff --git a/flexiapi/app/Http/Controllers/Admin/AccountImportController.php b/flexiapi/app/Http/Controllers/Admin/Account/ImportController.php
similarity index 99%
rename from flexiapi/app/Http/Controllers/Admin/AccountImportController.php
rename to flexiapi/app/Http/Controllers/Admin/Account/ImportController.php
index 46d8406..931b379 100644
--- a/flexiapi/app/Http/Controllers/Admin/AccountImportController.php
+++ b/flexiapi/app/Http/Controllers/Admin/Account/ImportController.php
@@ -17,7 +17,7 @@
along with this program. If not, see .
*/
-namespace App\Http\Controllers\Admin;
+namespace App\Http\Controllers\Admin\Account;
use App\Account;
use App\ExternalAccount;
@@ -31,7 +31,7 @@ use Illuminate\Support\Facades\Storage;
use Illuminate\Validation\Rules\File;
use Propaganistas\LaravelPhone\PhoneNumber;
-class AccountImportController extends Controller
+class ImportController extends Controller
{
private Collection $errors;
private string $importDirectory = 'imported_csv';
diff --git a/flexiapi/app/Http/Controllers/Admin/AccountStatisticsController.php b/flexiapi/app/Http/Controllers/Admin/Account/StatisticsController.php
similarity index 97%
rename from flexiapi/app/Http/Controllers/Admin/AccountStatisticsController.php
rename to flexiapi/app/Http/Controllers/Admin/Account/StatisticsController.php
index 98d6808..df0e58c 100644
--- a/flexiapi/app/Http/Controllers/Admin/AccountStatisticsController.php
+++ b/flexiapi/app/Http/Controllers/Admin/Account/StatisticsController.php
@@ -17,7 +17,7 @@
along with this program. If not, see .
*/
-namespace App\Http\Controllers\Admin;
+namespace App\Http\Controllers\Admin\Account;
use App\Account;
use App\Http\Controllers\Controller;
@@ -26,7 +26,7 @@ use App\StatisticsCall;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
-class AccountStatisticsController extends Controller
+class StatisticsController extends Controller
{
public function edit(Request $request, int $accountId)
{
diff --git a/flexiapi/app/Http/Controllers/Admin/AccountTypeController.php b/flexiapi/app/Http/Controllers/Admin/Account/TypeController.php
similarity index 97%
rename from flexiapi/app/Http/Controllers/Admin/AccountTypeController.php
rename to flexiapi/app/Http/Controllers/Admin/Account/TypeController.php
index 8e5a2b9..01ec834 100644
--- a/flexiapi/app/Http/Controllers/Admin/AccountTypeController.php
+++ b/flexiapi/app/Http/Controllers/Admin/Account/TypeController.php
@@ -17,7 +17,7 @@
along with this program. If not, see .
*/
-namespace App\Http\Controllers\Admin;
+namespace App\Http\Controllers\Admin\Account;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
@@ -27,7 +27,7 @@ use Illuminate\Validation\Rule;
use App\AccountType;
use App\Rules\NoUppercase;
-class AccountTypeController extends Controller
+class TypeController extends Controller
{
public function index()
{
diff --git a/flexiapi/app/Http/Controllers/Admin/Space/CardDavServerController.php b/flexiapi/app/Http/Controllers/Admin/Space/CardDavServerController.php
new file mode 100644
index 0000000..26257b8
--- /dev/null
+++ b/flexiapi/app/Http/Controllers/Admin/Space/CardDavServerController.php
@@ -0,0 +1,68 @@
+ $space,
+ 'carddavServer' => new SpaceCardDavServer
+ ]);
+ }
+
+ public function store(CardDavServer $request)
+ {
+ $carddavServer = new SpaceCardDavServer;
+ $carddavServer->space_id = $request->space->id;
+ $carddavServer->fill($request->validated());
+ $carddavServer->enabled = getRequestBoolean($request, 'enabled');
+ $carddavServer->use_exact_match_policy = getRequestBoolean($request, 'use_exact_match_policy');
+ $carddavServer->save();
+
+ return redirect()->route('admin.spaces.integration', $request->space);
+ }
+
+ public function edit(Space $space, int $carddavServerId)
+ {
+ return view('admin.space.carddav_server.create_edit', [
+ 'space' => $space,
+ 'carddavServer' => $space->carddavServers()->findOrFail($carddavServerId)
+ ]);
+ }
+
+ public function update(CardDavServer $request, Space $space, int $carddavServerId)
+ {
+ $carddavServer = $space->carddavServers()->findOrFail($carddavServerId);
+ $carddavServer->fill($request->validated());
+ $carddavServer->enabled = getRequestBoolean($request, 'enabled');
+ $carddavServer->use_exact_match_policy = getRequestBoolean($request, 'use_exact_match_policy');
+ $carddavServer->save();
+
+ return redirect()->route('admin.spaces.integration', $request->space);
+ }
+
+ public function delete(Space $space, int $carddavServerId)
+ {
+ return view('admin.space.carddav_server.delete', [
+ 'space' => $space,
+ 'carddavServer' => $space->carddavServers()->findOrFail($carddavServerId)
+ ]);
+ }
+
+ public function destroy(Space $space, int $carddavServerId)
+ {
+ $carddavServer = $space->carddavServers()->findOrFail($carddavServerId);
+ $carddavServer->delete();
+
+ return redirect()->route('admin.spaces.integration', $space->id);
+ }
+}
diff --git a/flexiapi/app/Http/Controllers/Admin/SpaceController.php b/flexiapi/app/Http/Controllers/Admin/SpaceController.php
index ddf2b85..9ec7e7c 100644
--- a/flexiapi/app/Http/Controllers/Admin/SpaceController.php
+++ b/flexiapi/app/Http/Controllers/Admin/SpaceController.php
@@ -146,6 +146,7 @@ class SpaceController extends Controller
$space->expire_at = $request->get('expire_at');
$space->custom_theme = getRequestBoolean($request, 'custom_theme');
$space->web_panel = getRequestBoolean($request, 'web_panel');
+ $space->carddav_user_credentials = getRequestBoolean($request, 'carddav_user_credentials');
$space->save();
return redirect()->route('admin.spaces.show', $space);
diff --git a/flexiapi/app/Http/Controllers/Api/Admin/AccountActionController.php b/flexiapi/app/Http/Controllers/Api/Admin/Account/ActionController.php
similarity index 96%
rename from flexiapi/app/Http/Controllers/Api/Admin/AccountActionController.php
rename to flexiapi/app/Http/Controllers/Api/Admin/Account/ActionController.php
index 154ddb5..1b7159d 100644
--- a/flexiapi/app/Http/Controllers/Api/Admin/AccountActionController.php
+++ b/flexiapi/app/Http/Controllers/Api/Admin/Account/ActionController.php
@@ -17,7 +17,7 @@
along with this program. If not, see .
*/
-namespace App\Http\Controllers\Api\Admin;
+namespace App\Http\Controllers\Api\Admin\Account;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
@@ -26,7 +26,7 @@ use App\Account;
use App\AccountAction;
use App\Rules\NoUppercase;
-class AccountActionController extends Controller
+class ActionController extends Controller
{
public function index(int $id)
{
diff --git a/flexiapi/app/Http/Controllers/Api/Admin/Account/CardDavCredentialsController.php b/flexiapi/app/Http/Controllers/Api/Admin/Account/CardDavCredentialsController.php
new file mode 100644
index 0000000..3393672
--- /dev/null
+++ b/flexiapi/app/Http/Controllers/Api/Admin/Account/CardDavCredentialsController.php
@@ -0,0 +1,78 @@
+space->accounts()->findOrFail($accountId);
+ $cardDavServers = $account->carddavServers;
+
+ if ($cardDavServers->isEmpty()) return new \stdClass;
+
+ return $cardDavServers->map(function ($cardDavServer) {
+ return $this->extractCardDavServer($cardDavServer);
+ })->keyBy('carddav_id');
+ }
+
+ public function show(Request $request, int $accountId, int $cardDavServerId)
+ {
+ $account = $request->space->accounts()->findOrFail($accountId);
+ $cardDavServer = $account->cardDavServers()->findOrFail($cardDavServerId);
+
+ return $this->extractCardDavServer($cardDavServer);
+ }
+
+ public function update(CardDavCredentials $request, int $accountId, int $cardDavServerId)
+ {
+ $account = $request->space->accounts()->findOrFail($accountId);
+ $cardDavServer = $request->space->cardDavServers()->findOrFail($cardDavServerId);
+
+ $accountCarddavCredentials = AccountCardDavCredentials::where('account_id', $account->id)
+ ->where('space_carddav_server_id', $cardDavServer->id)
+ ->delete();
+
+ $accountCarddavCredentials = new AccountCardDavCredentials;
+ $accountCarddavCredentials->space_carddav_server_id = $cardDavServer->id;
+ $accountCarddavCredentials->account_id = $account->id;
+ $accountCarddavCredentials->username = $request->get('username');
+ $accountCarddavCredentials->domain = $request->get('domain');
+ $accountCarddavCredentials->password = bchash(
+ $request->get('username'),
+ $request->get('domain'),
+ $request->get('password'),
+ $request->get('algorithm')
+ );
+ $accountCarddavCredentials->algorithm = $request->get('algorithm');
+ return $accountCarddavCredentials->save();
+ }
+
+ public function destroy(Request $request, int $accountId, int $cardDavServerId)
+ {
+ $account = $request->space->accounts()->findOrFail($accountId);
+ $cardDavServer = $account->cardDavServers()->findOrFail($cardDavServerId);
+
+ return $cardDavServer->delete();
+ }
+
+ private function extractCardDavServer(SpaceCardDavServer $cardDavServer)
+ {
+ return [
+ 'carddav_id' => $cardDavServer->id,
+ 'username' => $cardDavServer->pivot->username,
+ 'domain' => $cardDavServer->pivot->domain,
+ 'algorithm' => $cardDavServer->pivot->algorithm,
+ 'password' => $cardDavServer->pivot->password,
+ ];
+ }
+}
diff --git a/flexiapi/app/Http/Controllers/Api/Admin/AccountContactController.php b/flexiapi/app/Http/Controllers/Api/Admin/Account/ContactController.php
similarity index 95%
rename from flexiapi/app/Http/Controllers/Api/Admin/AccountContactController.php
rename to flexiapi/app/Http/Controllers/Api/Admin/Account/ContactController.php
index 7217efc..022d8f3 100644
--- a/flexiapi/app/Http/Controllers/Api/Admin/AccountContactController.php
+++ b/flexiapi/app/Http/Controllers/Api/Admin/Account/ContactController.php
@@ -17,13 +17,13 @@
along with this program. If not, see .
*/
-namespace App\Http\Controllers\Api\Admin;
+namespace App\Http\Controllers\Api\Admin\Account;
use App\Http\Controllers\Controller;
use App\Account;
-class AccountContactController extends Controller
+class ContactController extends Controller
{
public function index(int $id)
{
diff --git a/flexiapi/app/Http/Controllers/Api/Admin/AccountCreationTokenController.php b/flexiapi/app/Http/Controllers/Api/Admin/Account/CreationTokenController.php
similarity index 93%
rename from flexiapi/app/Http/Controllers/Api/Admin/AccountCreationTokenController.php
rename to flexiapi/app/Http/Controllers/Api/Admin/Account/CreationTokenController.php
index 477e354..3e5f2ef 100644
--- a/flexiapi/app/Http/Controllers/Api/Admin/AccountCreationTokenController.php
+++ b/flexiapi/app/Http/Controllers/Api/Admin/Account/CreationTokenController.php
@@ -17,7 +17,7 @@
along with this program. If not, see .
*/
-namespace App\Http\Controllers\Api\Admin;
+namespace App\Http\Controllers\Api\Admin\Account;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
@@ -26,7 +26,7 @@ use Illuminate\Support\Str;
use App\AccountCreationToken;
use App\Http\Controllers\Account\AuthenticateController as WebAuthenticateController;
-class AccountCreationTokenController extends Controller
+class CreationTokenController extends Controller
{
public function create(Request $request)
{
diff --git a/flexiapi/app/Http/Controllers/Api/Admin/AccountDictionaryController.php b/flexiapi/app/Http/Controllers/Api/Admin/Account/DictionaryController.php
similarity index 95%
rename from flexiapi/app/Http/Controllers/Api/Admin/AccountDictionaryController.php
rename to flexiapi/app/Http/Controllers/Api/Admin/Account/DictionaryController.php
index f5671f1..b4a1275 100644
--- a/flexiapi/app/Http/Controllers/Api/Admin/AccountDictionaryController.php
+++ b/flexiapi/app/Http/Controllers/Api/Admin/Account/DictionaryController.php
@@ -17,14 +17,14 @@
along with this program. If not, see .
*/
-namespace App\Http\Controllers\Api\Admin;
+namespace App\Http\Controllers\Api\Admin\Account;
use App\Http\Controllers\Controller;
use App\Account;
use Illuminate\Http\Request;
-class AccountDictionaryController extends Controller
+class DictionaryController extends Controller
{
public function index(int $accountId)
{
diff --git a/flexiapi/app/Http/Controllers/Api/Admin/AccountTypeController.php b/flexiapi/app/Http/Controllers/Api/Admin/Account/TypeController.php
similarity index 95%
rename from flexiapi/app/Http/Controllers/Api/Admin/AccountTypeController.php
rename to flexiapi/app/Http/Controllers/Api/Admin/Account/TypeController.php
index 59b6676..e9f25b2 100644
--- a/flexiapi/app/Http/Controllers/Api/Admin/AccountTypeController.php
+++ b/flexiapi/app/Http/Controllers/Api/Admin/Account/TypeController.php
@@ -17,7 +17,7 @@
along with this program. If not, see .
*/
-namespace App\Http\Controllers\Api\Admin;
+namespace App\Http\Controllers\Api\Admin\Account;
use App\Http\Controllers\Controller;
use App\Rules\NoUppercase;
@@ -25,7 +25,7 @@ use Illuminate\Http\Request;
use App\AccountType;
-class AccountTypeController extends Controller
+class TypeController extends Controller
{
public function index()
{
diff --git a/flexiapi/app/Http/Controllers/Api/Admin/Space/CardDavServerController.php b/flexiapi/app/Http/Controllers/Api/Admin/Space/CardDavServerController.php
new file mode 100644
index 0000000..9f596d3
--- /dev/null
+++ b/flexiapi/app/Http/Controllers/Api/Admin/Space/CardDavServerController.php
@@ -0,0 +1,56 @@
+firstOrFail()->carddavServers;
+ }
+
+ public function show(string $host, int $carddavServerId)
+ {
+ return Space::where('host', $host)->firstOrFail()->carddavServers()->findOrFail($carddavServerId);
+ }
+
+ public function store(CardDavServer $request, string $host)
+ {
+ $space = Space::where('host', $host)->firstOrFail();
+
+ $carddavServer = new SpaceCardDavServer;
+ $carddavServer->space_id = $space->id;
+ $carddavServer->fill($request->validated());
+ $carddavServer->enabled = $request->has('enabled') && (bool)$request->get('enabled');
+ $carddavServer->use_exact_match_policy = $request->has('use_exact_match_policy') && (bool)$request->get('use_exact_match_policy');
+
+ return $carddavServer->save();
+ }
+
+ public function update(CardDavServer $request, string $host, int $carddavServerId)
+ {
+ $space = Space::where('host', $host)->firstOrFail();
+
+ $carddavServer = $space->carddavServers()->findOrFail($carddavServerId);
+ $carddavServer->fill($request->validated());
+ $carddavServer->enabled = $request->has('enabled') && (bool)$request->get('enabled');
+ $carddavServer->use_exact_match_policy = $request->has('use_exact_match_policy') && (bool)$request->get('use_exact_match_policy');
+
+ return $carddavServer->save();
+ }
+
+ public function destroy(string $host, int $carddavServerId)
+ {
+ $space = Space::where('host', $host)->firstOrFail();
+
+ $carddavServer = $space->carddavServers()->findOrFail($carddavServerId);
+ return $carddavServer->delete();
+ }
+}
diff --git a/flexiapi/app/Http/Controllers/Api/Admin/EmailServerController.php b/flexiapi/app/Http/Controllers/Api/Admin/Space/EmailServerController.php
similarity index 96%
rename from flexiapi/app/Http/Controllers/Api/Admin/EmailServerController.php
rename to flexiapi/app/Http/Controllers/Api/Admin/Space/EmailServerController.php
index 11f9f2f..5e4298f 100644
--- a/flexiapi/app/Http/Controllers/Api/Admin/EmailServerController.php
+++ b/flexiapi/app/Http/Controllers/Api/Admin/Space/EmailServerController.php
@@ -1,6 +1,6 @@
name = $request->get('name');
- $space->domain = $request->get('domain');
- $space->host = $request->get('host');
- $this->setRequestBoolean($request, $space, 'super');
- $this->setRequestBoolean($request, $space, 'disable_chat_feature');
- $this->setRequestBoolean($request, $space, 'disable_meetings_feature');
- $this->setRequestBoolean($request, $space, 'disable_broadcast_feature');
- $this->setRequestBoolean($request, $space, 'hide_settings');
- $this->setRequestBoolean($request, $space, 'hide_account_settings');
- $this->setRequestBoolean($request, $space, 'disable_call_recordings_feature');
- $this->setRequestBoolean($request, $space, 'only_display_sip_uri_username');
- $this->setRequestBoolean($request, $space, 'assistant_hide_create_account');
- $this->setRequestBoolean($request, $space, 'assistant_disable_qr_code');
- $this->setRequestBoolean($request, $space, 'assistant_hide_third_party_account');
- $space->max_account = $request->get('max_account', 0);
- $space->max_accounts = $request->get('max_accounts', 0);
- $space->expire_at = $request->get('expire_at');
-
- $space->copyright_text = $request->get('copyright_text');
- $space->intro_registration_text = $request->get('intro_registration_text');
- $space->newsletter_registration_address = $request->get('newsletter_registration_address');
$space->account_proxy_registrar_address = $request->get('account_proxy_registrar_address');
$space->account_realm = $request->get('account_realm');
+ $space->copyright_text = $request->get('copyright_text');
$space->custom_provisioning_entries = $request->get('custom_provisioning_entries');
+ $space->domain = $request->get('domain');
+ $space->expire_at = $request->get('expire_at');
+ $space->host = $request->get('host');
+ $space->intro_registration_text = $request->get('intro_registration_text');
+ $space->max_account = $request->get('max_account', 0);
+ $space->max_accounts = $request->get('max_accounts', 0);
+ $space->name = $request->get('name');
+ $space->newsletter_registration_address = $request->get('newsletter_registration_address');
+ $this->setRequestBoolean($request, $space, 'assistant_disable_qr_code');
+ $this->setRequestBoolean($request, $space, 'assistant_hide_create_account');
+ $this->setRequestBoolean($request, $space, 'assistant_hide_third_party_account');
+ $this->setRequestBoolean($request, $space, 'carddav_user_credentials');
$this->setRequestBoolean($request, $space, 'custom_provisioning_overwrite_all');
- $this->setRequestBoolean($request, $space, 'provisioning_use_linphone_provisioning_header');
$this->setRequestBoolean($request, $space, 'custom_theme');
- $this->setRequestBoolean($request, $space, 'web_panel');
- $this->setRequestBoolean($request, $space, 'public_registration');
- $this->setRequestBoolean($request, $space, 'phone_registration');
+ $this->setRequestBoolean($request, $space, 'disable_broadcast_feature');
+ $this->setRequestBoolean($request, $space, 'disable_call_recordings_feature');
+ $this->setRequestBoolean($request, $space, 'disable_chat_feature');
+ $this->setRequestBoolean($request, $space, 'disable_meetings_feature');
+ $this->setRequestBoolean($request, $space, 'hide_account_settings');
+ $this->setRequestBoolean($request, $space, 'hide_settings');
$this->setRequestBoolean($request, $space, 'intercom_features');
-
+ $this->setRequestBoolean($request, $space, 'only_display_sip_uri_username');
+ $this->setRequestBoolean($request, $space, 'phone_registration');
+ $this->setRequestBoolean($request, $space, 'provisioning_use_linphone_provisioning_header');
+ $this->setRequestBoolean($request, $space, 'public_registration');
+ $this->setRequestBoolean($request, $space, 'super');
+ $this->setRequestBoolean($request, $space, 'web_panel');
$space->save();
return $space->refresh();
@@ -90,30 +89,30 @@ class SpaceController extends Controller
public function update(Request $request, string $domain)
{
$request->validate([
- 'super' => 'required|boolean',
- 'disable_chat_feature' => 'required|boolean',
- 'disable_meetings_feature' => 'required|boolean',
- 'disable_broadcast_feature' => 'required|boolean',
- 'hide_settings' => 'required|boolean',
- 'hide_account_settings' => 'required|boolean',
- 'disable_call_recordings_feature' => 'required|boolean',
- 'only_display_sip_uri_username' => 'required|boolean',
- 'assistant_hide_create_account' => 'required|boolean',
- 'assistant_disable_qr_code' => 'required|boolean',
- 'assistant_hide_third_party_account' => 'required|boolean',
- 'max_account' => 'required|integer',
- 'max_accounts' => 'required|integer',
- 'expire_at' => 'nullable|date|after_or_equal:today',
'account_realm' => ['nullable', new Domain()],
-
+ 'assistant_disable_qr_code' => 'required|boolean',
+ 'assistant_hide_create_account' => 'required|boolean',
+ 'assistant_hide_third_party_account' => 'required|boolean',
+ 'carddav_user_credentials' => 'required|boolean',
'custom_provisioning_entries' => ['nullable', new Ini(Space::FORBIDDEN_KEYS)],
'custom_provisioning_overwrite_all' => 'required|boolean',
- 'provisioning_use_linphone_provisioning_header' => 'required|boolean',
'custom_theme' => 'required|boolean',
- 'web_panel' => 'required|boolean',
- 'public_registration' => 'required|boolean',
- 'phone_registration' => 'required|boolean',
+ 'disable_broadcast_feature' => 'required|boolean',
+ 'disable_call_recordings_feature' => 'required|boolean',
+ 'disable_chat_feature' => 'required|boolean',
+ 'disable_meetings_feature' => 'required|boolean',
+ 'expire_at' => 'nullable|date|after_or_equal:today',
+ 'hide_account_settings' => 'required|boolean',
+ 'hide_settings' => 'required|boolean',
'intercom_features' => 'required|boolean',
+ 'max_account' => 'required|integer',
+ 'max_accounts' => 'required|integer',
+ 'only_display_sip_uri_username' => 'required|boolean',
+ 'phone_registration' => 'required|boolean',
+ 'provisioning_use_linphone_provisioning_header' => 'required|boolean',
+ 'public_registration' => 'required|boolean',
+ 'super' => 'required|boolean',
+ 'web_panel' => 'required|boolean',
]);
$space = Space::where('domain', $domain)->firstOrFail();
diff --git a/flexiapi/app/Http/Kernel.php b/flexiapi/app/Http/Kernel.php
index cf9cab1..4b9a831 100644
--- a/flexiapi/app/Http/Kernel.php
+++ b/flexiapi/app/Http/Kernel.php
@@ -72,30 +72,31 @@ class Kernel extends HttpKernel
* @var array
*/
protected $middlewareAliases = [
- 'auth' => \App\Http\Middleware\Authenticate::class,
'auth.admin' => \App\Http\Middleware\AuthenticateAdmin::class,
- 'auth.super_admin' => \App\Http\Middleware\AuthenticateSuperAdmin::class,
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
+ 'auth.check_blocked' => \App\Http\Middleware\CheckBlocked::class,
'auth.digest_or_key' => \App\Http\Middleware\AuthenticateDigestOrKey::class,
'auth.jwt' => \App\Http\Middleware\AuthenticateJWT::class,
- 'auth.check_blocked' => \App\Http\Middleware\CheckBlocked::class,
- 'validate_json' => \App\Http\Middleware\ValidateJSON::class,
- 'web_panel_enabled' => \App\Http\Middleware\IsWebPanelEnabled::class,
- 'public_registration' => \App\Http\Middleware\IsPublicRegistration::class,
- 'phone_registration' => \App\Http\Middleware\IsPhoneRegistration::class,
- 'intercom_features' => \App\Http\Middleware\IsIntercomFeatures::class,
+ 'auth.super_admin' => \App\Http\Middleware\AuthenticateSuperAdmin::class,
+ 'auth' => \App\Http\Middleware\Authenticate::class,
'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,
'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
'can' => \Illuminate\Auth\Middleware\Authorize::class,
- 'cookie' => \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
'cookie.encrypt' => \App\Http\Middleware\EncryptCookies::class,
+ 'cookie' => \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
+ 'feature.carddav_user_credentials' => \App\Http\Middleware\IsCardDavCredentialsEnabled::class,
+ 'feature.intercom' => \App\Http\Middleware\IsIntercomFeatures::class,
+ 'feature.phone_registration' => \App\Http\Middleware\IsPhoneRegistration::class,
+ 'feature.public_registration' => \App\Http\Middleware\IsPublicRegistration::class,
+ 'feature.web_panel_enabled' => \App\Http\Middleware\IsWebPanelEnabled::class,
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
+ 'localization' => \App\Http\Middleware\Localization::class,
'password.confirm' => \Illuminate\Auth\Middleware\RequirePassword::class,
'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class,
'space.check' => \App\Http\Middleware\SpaceCheck::class,
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
+ 'validate_json' => \App\Http\Middleware\ValidateJSON::class,
'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
- 'localization' => \App\Http\Middleware\Localization::class,
];
/**
diff --git a/flexiapi/app/Http/Middleware/AuthenticateAdmin.php b/flexiapi/app/Http/Middleware/AuthenticateAdmin.php
index 83133de..ca94aa1 100644
--- a/flexiapi/app/Http/Middleware/AuthenticateAdmin.php
+++ b/flexiapi/app/Http/Middleware/AuthenticateAdmin.php
@@ -29,7 +29,7 @@ class AuthenticateAdmin
return redirect()->route('account.login');
}
- if (!$request->user()->admin) {
+ if (!$request->user()->admin || $request->user()->domain != $request->space->domain) {
return abort(403, 'Unauthorized area');
}
diff --git a/flexiapi/app/Http/Middleware/IsCardDavCredentialsEnabled.php b/flexiapi/app/Http/Middleware/IsCardDavCredentialsEnabled.php
new file mode 100644
index 0000000..b62c102
--- /dev/null
+++ b/flexiapi/app/Http/Middleware/IsCardDavCredentialsEnabled.php
@@ -0,0 +1,19 @@
+space->carddav_user_credentials) {
+ return $next($request);
+ }
+
+ return abort(403, 'CardDav Credentials features disabled');
+ }
+}
diff --git a/flexiapi/app/Http/Requests/Account/CardDavCredentials.php b/flexiapi/app/Http/Requests/Account/CardDavCredentials.php
new file mode 100644
index 0000000..403e8de
--- /dev/null
+++ b/flexiapi/app/Http/Requests/Account/CardDavCredentials.php
@@ -0,0 +1,39 @@
+.
+*/
+
+namespace App\Http\Requests\Account;
+
+use Illuminate\Foundation\Http\FormRequest;
+use Illuminate\Validation\Rule;
+
+use App\Rules\Domain;
+use App\Rules\PasswordAlgorithm;
+
+class CardDavCredentials extends FormRequest
+{
+ public function rules()
+ {
+ return [
+ 'username' => 'required|min:3',
+ 'password' => 'required',
+ 'algorithm' => ['required', new PasswordAlgorithm],
+ 'domain' => ['required', new Domain],
+ ];
+ }
+}
diff --git a/flexiapi/app/Http/Requests/Space/CardDavServer.php b/flexiapi/app/Http/Requests/Space/CardDavServer.php
new file mode 100644
index 0000000..16c95c5
--- /dev/null
+++ b/flexiapi/app/Http/Requests/Space/CardDavServer.php
@@ -0,0 +1,42 @@
+.
+*/
+
+namespace App\Http\Requests\Space;
+
+use Illuminate\Foundation\Http\FormRequest;
+use Illuminate\Validation\Rule;
+
+use App\EmailServer;
+use App\Rules\CommaList;
+
+class CardDavServer extends FormRequest
+{
+ public function rules()
+ {
+ return [
+ 'uri' => 'required|url',
+ 'min_characters' => 'integer|min:1',
+ 'results_limit' => 'integer|min:0',
+ 'timeout' => 'integer|min:1',
+ 'delay' => 'integer|min:100',
+ 'fields_for_user_input' => [new CommaList],
+ 'fields_for_domain' => [new CommaList],
+ ];
+ }
+}
diff --git a/flexiapi/app/Rules/CommaList.php b/flexiapi/app/Rules/CommaList.php
new file mode 100644
index 0000000..e01a0c5
--- /dev/null
+++ b/flexiapi/app/Rules/CommaList.php
@@ -0,0 +1,21 @@
+ 'boolean',
+ 'assistant_disable_qr_code' => 'boolean',
+ 'assistant_hide_create_account' => 'boolean',
+ 'assistant_hide_third_party_account' => 'boolean',
+ 'carddav_user_credentials' => 'boolean',
+ 'disable_broadcast_feature' => 'boolean',
+ 'disable_call_recordings_feature' => 'boolean',
'disable_chat_feature' => 'boolean',
'disable_meetings_feature' => 'boolean',
- 'disable_broadcast_feature' => 'boolean',
- 'hide_settings' => 'boolean',
- 'hide_account_settings' => 'boolean',
- 'disable_call_recordings_feature' => 'boolean',
- 'only_display_sip_uri_username' => 'boolean',
- 'assistant_hide_create_account' => 'boolean',
- 'assistant_disable_qr_code' => 'boolean',
- 'assistant_hide_third_party_account' => 'boolean',
'expire_at' => 'date',
+ 'hide_account_settings' => 'boolean',
+ 'hide_settings' => 'boolean',
+ 'only_display_sip_uri_username' => 'boolean',
+ 'super' => 'boolean',
];
public const HOST_REGEX = '[\w\-]+';
public const DOMAIN_REGEX = '(?=^.{4,253}$)(^((?!-)[a-z0-9-]{1,63}(?superAdmin) {
+ return;
+ }
+
+ $builder->where('domain', Auth::user()->domain);
+ });
+ }
+
public function accounts()
{
return $this->hasMany(Account::class, 'domain', 'domain');
@@ -82,6 +97,11 @@ class Space extends Model
return $this->hasOne(SpaceEmailServer::class);
}
+ public function carddavServers()
+ {
+ return $this->hasMany(SpaceCardDavServer::class);
+ }
+
public function scopeNotFull(Builder $query)
{
return $query->where('max_accounts', 0)
diff --git a/flexiapi/app/SpaceCardDavServer.php b/flexiapi/app/SpaceCardDavServer.php
new file mode 100644
index 0000000..ef4fd2a
--- /dev/null
+++ b/flexiapi/app/SpaceCardDavServer.php
@@ -0,0 +1,82 @@
+ 'boolean',
+ 'use_exact_match_policy' => 'boolean',
+ ];
+
+ public function space()
+ {
+ return $this->belongsTo(Space::class);
+ }
+
+ public function accounts()
+ {
+ return $this->belongsToMany(Account::class, 'account_carddav_credentials', 'space_carddav_server_id', 'account_id');
+ }
+
+ public function getNameAttribute()
+ {
+ return __('CardDav Server') . ' ' . $this->id;
+ }
+
+ public function getProvisioningSection($config, int $remoteContactDirectoryCounter)
+ {
+ $dom = $config->ownerDocument;
+
+ $section = $dom->createElement('section');
+ $section->setAttribute('name', 'remote_contact_directory_' . $remoteContactDirectoryCounter);
+
+ $entry = $dom->createElement('entry', $this->enabled ? '1': '0');
+ $entry->setAttribute('name', 'enabled');
+ $section->appendChild($entry);
+
+ $entry = $dom->createElement('entry', 'carddav');
+ $entry->setAttribute('name', 'type');
+ $section->appendChild($entry);
+
+ $entry = $dom->createElement('entry', $this->uri);
+ $entry->setAttribute('name', 'uri');
+ $section->appendChild($entry);
+
+ $entry = $dom->createElement('entry', $this->min_characters);
+ $entry->setAttribute('name', 'min_characters');
+ $section->appendChild($entry);
+
+ $entry = $dom->createElement('entry', $this->results_limit);
+ $entry->setAttribute('name', 'results_limit');
+ $section->appendChild($entry);
+
+ $entry = $dom->createElement('entry', $this->timeout);
+ $entry->setAttribute('name', 'timeout');
+ $section->appendChild($entry);
+
+ $entry = $dom->createElement('entry', $this->delay);
+ $entry->setAttribute('name', 'delay');
+ $section->appendChild($entry);
+
+ $entry = $dom->createElement('entry', $this->fields_for_user_input);
+ $entry->setAttribute('name', 'carddav_fields_for_user_input');
+ $section->appendChild($entry);
+
+ $entry = $dom->createElement('entry', $this->fields_for_domain);
+ $entry->setAttribute('name', 'carddav_fields_for_domain');
+ $section->appendChild($entry);
+
+ $entry = $dom->createElement('entry', $this->use_exact_match_policy ? '1': '0');
+ $entry->setAttribute('name', 'carddav_use_exact_match_policy');
+ $section->appendChild($entry);
+
+ $config->appendChild($section);
+ }
+}
diff --git a/flexiapi/config/session.php b/flexiapi/config/session.php
index 290fa69..3e9082d 100644
--- a/flexiapi/config/session.php
+++ b/flexiapi/config/session.php
@@ -18,7 +18,7 @@ return [
|
*/
- 'driver' => env('SESSION_DRIVER', 'cookie'),
+ 'driver' => env('SESSION_DRIVER', 'file'),
/*
|--------------------------------------------------------------------------
diff --git a/flexiapi/database/migrations/2025_08_20_083715_create_space_carddav_servers.php b/flexiapi/database/migrations/2025_08_20_083715_create_space_carddav_servers.php
new file mode 100644
index 0000000..f325f8f
--- /dev/null
+++ b/flexiapi/database/migrations/2025_08_20_083715_create_space_carddav_servers.php
@@ -0,0 +1,62 @@
+boolean('carddav_user_credentials')->default(false);
+ });
+
+ Schema::create('space_carddav_servers', function (Blueprint $table) {
+ $table->id();
+
+ $table->bigInteger('space_id')->unsigned();
+ $table->foreign('space_id')->references('id')
+ ->on('spaces')->onDelete('cascade');
+
+ $table->boolean('enabled')->default(true);
+ $table->string('uri');
+ $table->integer('min_characters')->default(3);
+ $table->integer('results_limit')->default(0);
+ $table->integer('timeout')->default(5);
+ $table->integer('delay')->default(500);
+ $table->string('fields_for_user_input')->nullable();
+ $table->string('fields_for_domain')->nullable();
+ $table->boolean('use_exact_match_policy')->default(false);
+ $table->timestamps();
+ });
+
+ Schema::create('account_carddav_credentials', function (Blueprint $table) {
+ $table->string('username', 64);
+ $table->string('password', 255);
+ $table->string('domain', 255);
+ $table->string('algorithm', 10)->default('MD5');
+
+ $table->bigInteger('space_carddav_server_id')->unsigned();
+ $table->foreign('space_carddav_server_id')->references('id')
+ ->on('space_carddav_servers')->onDelete('cascade');
+
+ $table->integer('account_id')->unsigned();
+ $table->foreign('account_id')->references('id')
+ ->on('accounts')->onDelete('cascade');
+ $table->timestamps();
+
+ $table->unique(['space_carddav_server_id', 'account_id'], 'account_carddav_credentials_unique');
+ });
+ }
+
+ public function down(): void
+ {
+ Schema::table('spaces', function (Blueprint $table) {
+ $table->dropColumn('carddav_user_credentials');
+ });
+
+ Schema::dropIfExists('account_carddav_credentials');
+ Schema::dropIfExists('space_carddav_servers');
+ }
+};
diff --git a/flexiapi/lang/fr.json b/flexiapi/lang/fr.json
index a5ad652..47a70d5 100644
--- a/flexiapi/lang/fr.json
+++ b/flexiapi/lang/fr.json
@@ -41,6 +41,9 @@
"Calls logs": "Journaux d'appel",
"Cancel": "Annuler",
"Cannot be changed once created.": "Ne peut être changé par la suite.",
+ "CardDav credentials": "Identification CardDav",
+ "CardDav Server": "Serveur CardDav",
+ "CardDav Servers": "Serveurs CardDav",
"Change your email": "Changer votre email",
"Change your phone number": "Changer votre numéro de téléphone",
"Check the README.md documentation": "Voir la documentation dans README.md",
@@ -71,6 +74,7 @@
"Day": "Jour",
"Deactivate All": "Tout désactiver",
"Deactivated": "Désactivé",
+ "Delay in milliseconds before submiting the request": "Temps en millisecondes avant d'envoyer la requête",
"Delete": "Supprimer",
"Description": "Description",
"Devices": "Appareils",
@@ -84,6 +88,7 @@
"Email": "Email",
"Empty": "Vide",
"Enable the web interface": "Activer l'interface web",
+ "Enable user credentials for CardDav servers": "Activer les identifiants utilisateur pour les serveurs CardDav",
"Enabled": "Activé",
"Encrypted": "Chiffré",
"Enter the code you received below": "Saisissez le code reçu ci-dessous",
@@ -118,6 +123,9 @@
"Key": "Clef",
"Last used": "Dernière utilisation",
"Leave empty to create a root Space.": "Laisser vide si vous souhaitez créer un Espace à la racine",
+ "Limit the number of results": "Limiter le nomber de résultats",
+ "List of vcard fields to match for SIP domain": "Liste des champs vcard à matcher pour le domaine SIP",
+ "List of vcard fields to match with user input": "Liste des champs vcard à matcher avec les entrées utilisateur",
"Login to my account":"Connexion à mon compte",
"Login using a QRCode": "S'authentifier avec un QRCode",
"Login": "Authentification",
@@ -125,6 +133,7 @@
"Markdown text": "Texte Markdown",
"Max accounts": "Maximum de comptes",
"Meeting": "Réunions",
+ "Min characters to search": "Nombre minimum de caractères pour chercher",
"Month": "Mois",
"My Account": "Mon Compte",
"My Space": "Mon Espace",
@@ -168,6 +177,7 @@
"Remote provisioning": "Configuration distante",
"Remove": "Remove",
"Renew": "Renouveller",
+ "Request timeout in seconds": "Expiration de la requête en secondes",
"Requests": "Requêtes",
"Resend": "Renvoyer",
"Reset my password":"Réinitialiser mon mot de passe",
@@ -185,6 +195,7 @@
"Send an email to the user to reset the password": "Envoyer un email à l'utilisateur pour réinitialiser son mot de passe",
"Send an email to the user with provisioning information": "Envoyer un email à l'utilisateur avec les informations de déploiement",
"Send": "Envoyer",
+ "Separated by commas": "Séparé par des virgules",
"Settings": "Paramètres",
"Show usernames only": "Afficher uniquement les noms d'utilisateur",
"SIP address":"Adresse SIP",
@@ -224,6 +235,7 @@
"Updated on": "Mis à jour le",
"Updated": "Mise à jour",
"Use ; to comment, key=\"value\" to declare a complex string.": "Utilisez ; pour commenter, clef=\"valeur\" pour déclarer une chaine complexe.",
+ "Use exact match policy": "Utiliser la rêgle de recherche exacte",
"Use the mobile app to recover your account using your phone number": "Utilisez l'application mobile pour récupérer votre compte avec votre numéro de téléphone",
"Used on": "Utilisé le",
"User": "Utilisateur",
@@ -243,6 +255,7 @@
"Welcome on :app_name": "Bienvenue sur :app_name",
"Welcome to :space: Start using your account today": "Bienvenue sur :space : commencez à utiliser votre compte dès maintenant",
"Welcome to :space":"Bienvenue sur :space",
+ "Whether match must be exact or approximate (ignoring case, accents…)": "Est-ce que la recherche doit être exacte ou approximative (ignorer les majuscules, les accents…)",
"Year": "Année",
"You already have an account?": "Vous avez déjà un compte ?",
"You are going to permanently delete the following element. Please confirm your action.": "Vous allez supprimer l'élément suivant. Veuillez confirmer votre action.",
diff --git a/flexiapi/resources/views/admin/account/account_type/create.blade.php b/flexiapi/resources/views/admin/account/account_type/create.blade.php
index 7e4a319..846f280 100644
--- a/flexiapi/resources/views/admin/account/account_type/create.blade.php
+++ b/flexiapi/resources/views/admin/account/account_type/create.blade.php
@@ -1,8 +1,7 @@
@extends('layouts.main')
@section('breadcrumb')
- @include('admin.account.parts.breadcrumb_accounts_index')
- @include('admin.account.parts.breadcrumb_accounts_show', ['account' => $account])
+ @include('admin.parts.breadcrumb.accounts.show', ['account' => $account])
{{ __('Types') }}
diff --git a/flexiapi/resources/views/admin/account/action/create_edit.blade.php b/flexiapi/resources/views/admin/account/action/create_edit.blade.php
index 913636f..553beda 100644
--- a/flexiapi/resources/views/admin/account/action/create_edit.blade.php
+++ b/flexiapi/resources/views/admin/account/action/create_edit.blade.php
@@ -1,8 +1,7 @@
@extends('layouts.main')
@section('breadcrumb')
- @include('admin.account.parts.breadcrumb_accounts_index')
- @include('admin.account.parts.breadcrumb_accounts_show', ['account' => $account])
+ @include('admin.parts.breadcrumb.accounts.show', ['account' => $account])
{{ __('Actions') }}
diff --git a/flexiapi/resources/views/admin/account/action/delete.blade.php b/flexiapi/resources/views/admin/account/action/delete.blade.php
index 1fd781d..28fee76 100644
--- a/flexiapi/resources/views/admin/account/action/delete.blade.php
+++ b/flexiapi/resources/views/admin/account/action/delete.blade.php
@@ -1,8 +1,7 @@
@extends('layouts.main')
@section('breadcrumb')
- @include('admin.account.parts.breadcrumb_accounts_index')
- @include('admin.account.parts.breadcrumb_accounts_show', ['account' => $account])
+ @include('admin.parts.breadcrumb.accounts.show', ['account' => $account])
{{ __('Actions') }}
diff --git a/flexiapi/resources/views/admin/account/activity/index.blade.php b/flexiapi/resources/views/admin/account/activity/index.blade.php
index c8a4ab4..dcdedb6 100644
--- a/flexiapi/resources/views/admin/account/activity/index.blade.php
+++ b/flexiapi/resources/views/admin/account/activity/index.blade.php
@@ -1,8 +1,7 @@
@extends('layouts.main')
@section('breadcrumb')
- @include('admin.account.parts.breadcrumb_accounts_index')
- @include('admin.account.parts.breadcrumb_accounts_show', ['account' => $account])
+ @include('admin.parts.breadcrumb.accounts.show', ['account' => $account])
Activity
@endsection
diff --git a/flexiapi/resources/views/admin/account/carddav/create.blade.php b/flexiapi/resources/views/admin/account/carddav/create.blade.php
new file mode 100644
index 0000000..a2625f1
--- /dev/null
+++ b/flexiapi/resources/views/admin/account/carddav/create.blade.php
@@ -0,0 +1,54 @@
+@extends('layouts.main')
+
+@section('breadcrumb')
+ @include('admin.parts.breadcrumb.accounts.show', ['account' => $account])
+
+ {{ __('CardDav credentials') }}
+
+@endsection
+
+@section('content')
+ {{ __('CardDav credentials') }}
+
+
+@endsection
diff --git a/flexiapi/resources/views/admin/account/carddav/delete.blade.php b/flexiapi/resources/views/admin/account/carddav/delete.blade.php
new file mode 100644
index 0000000..d01a5ad
--- /dev/null
+++ b/flexiapi/resources/views/admin/account/carddav/delete.blade.php
@@ -0,0 +1,31 @@
+@extends('layouts.main')
+
+@section('breadcrumb')
+ @include('admin.parts.breadcrumb.accounts.show', ['account' => $account])
+
+ {{ __('CardDav credentials') }}
+
+ {{ __('Delete') }}
+@endsection
+
+@section('content')
+ {{ __('CardDav credentials') }}
+
+
+@endsection
diff --git a/flexiapi/resources/views/admin/account/contact/create.blade.php b/flexiapi/resources/views/admin/account/contact/create.blade.php
index 3c97f37..ce1fb0e 100644
--- a/flexiapi/resources/views/admin/account/contact/create.blade.php
+++ b/flexiapi/resources/views/admin/account/contact/create.blade.php
@@ -1,8 +1,7 @@
@extends('layouts.main')
@section('breadcrumb')
- @include('admin.account.parts.breadcrumb_accounts_index')
- @include('admin.account.parts.breadcrumb_accounts_show', ['account' => $account])
+ @include('admin.parts.breadcrumb.accounts.show', ['account' => $account])
{{ __('Add contact') }}
@endsection
diff --git a/flexiapi/resources/views/admin/account/contact/delete.blade.php b/flexiapi/resources/views/admin/account/contact/delete.blade.php
index 4dadb0b..e047aa6 100644
--- a/flexiapi/resources/views/admin/account/contact/delete.blade.php
+++ b/flexiapi/resources/views/admin/account/contact/delete.blade.php
@@ -1,8 +1,7 @@
@extends('layouts.main')
@section('breadcrumb')
- @include('admin.account.parts.breadcrumb_accounts_index')
- @include('admin.account.parts.breadcrumb_accounts_show', ['account' => $account])
+ @include('admin.parts.breadcrumb.accounts.show', ['account' => $account])
{{ __('Contacts') }}
diff --git a/flexiapi/resources/views/admin/account/contact/index.blade.php b/flexiapi/resources/views/admin/account/contact/index.blade.php
index 2cd5503..afad8f2 100644
--- a/flexiapi/resources/views/admin/account/contact/index.blade.php
+++ b/flexiapi/resources/views/admin/account/contact/index.blade.php
@@ -1,8 +1,7 @@
@extends('layouts.main')
@section('breadcrumb')
- @include('admin.account.parts.breadcrumb_accounts_index')
- @include('admin.account.parts.breadcrumb_accounts_show', ['account' => $account])
+ @include('admin.parts.breadcrumb.accounts.show', ['account' => $account])
{{ __('Contacts') }}
@endsection
diff --git a/flexiapi/resources/views/admin/account/create_edit.blade.php b/flexiapi/resources/views/admin/account/create_edit.blade.php
index 6d63095..46acd49 100644
--- a/flexiapi/resources/views/admin/account/create_edit.blade.php
+++ b/flexiapi/resources/views/admin/account/create_edit.blade.php
@@ -1,9 +1,11 @@
@extends('layouts.main')
@section('breadcrumb')
- @include('admin.account.parts.breadcrumb_accounts_index')
+ @include('admin.parts.breadcrumb.accounts.index')
@if ($account->id)
- @include('admin.account.parts.breadcrumb_accounts_show', ['account' => $account])
+
+ {{ $account->identifier }}
+
{{ __('Edit') }}
@else
{{ __('Create') }}
diff --git a/flexiapi/resources/views/admin/account/delete.blade.php b/flexiapi/resources/views/admin/account/delete.blade.php
index 7e7e01f..7b6988d 100644
--- a/flexiapi/resources/views/admin/account/delete.blade.php
+++ b/flexiapi/resources/views/admin/account/delete.blade.php
@@ -1,8 +1,7 @@
@extends('layouts.main')
@section('breadcrumb')
- @include('admin.account.parts.breadcrumb_accounts_index')
- @include('admin.account.parts.breadcrumb_accounts_show', ['account' => $account])
+ @include('admin.parts.breadcrumb.accounts.show', ['account' => $account])
{{ __('Delete') }}
@endsection
diff --git a/flexiapi/resources/views/admin/account/device/delete.blade.php b/flexiapi/resources/views/admin/account/device/delete.blade.php
index 2dce50b..af50d51 100644
--- a/flexiapi/resources/views/admin/account/device/delete.blade.php
+++ b/flexiapi/resources/views/admin/account/device/delete.blade.php
@@ -1,13 +1,12 @@
@extends('layouts.main')
@section('breadcrumb')
- @include('admin.account.parts.breadcrumb_accounts_index')
- @include('admin.account.parts.breadcrumb_accounts_show', ['account' => $account])
+ @include('admin.parts.breadcrumb.accounts.show', ['account' => $account])
{{ __('Delete') }}
@endsection
@section('content')
- {{ __('Delete') }}
+ {{ __('Delete') }}
{{ __('You are going to permanently delete the following element. Please confirm your action.') }}
diff --git a/flexiapi/resources/views/admin/account/dictionary/create_edit.blade.php b/flexiapi/resources/views/admin/account/dictionary/create_edit.blade.php
index 992e907..c490462 100644
--- a/flexiapi/resources/views/admin/account/dictionary/create_edit.blade.php
+++ b/flexiapi/resources/views/admin/account/dictionary/create_edit.blade.php
@@ -1,17 +1,12 @@
@extends('layouts.main')
@section('breadcrumb')
- @include('admin.account.parts.breadcrumb_accounts_index')
- @include('admin.account.parts.breadcrumb_accounts_show', ['account' => $account])
+ @include('admin.parts.breadcrumb.accounts.show', ['account' => $account])
{{ __('Dictionary') }}
@endsection
@section('content')
- @if ($entry->id)
-
{{ __('Edit') }}
- @else
-
{{ __('Create') }}
- @endif
+
{{ __('Dictionary') }}