feat: add notion of _me_ as a contact (#2899)
This commit is contained in:
@@ -2,10 +2,12 @@
|
||||
|
||||
### New features:
|
||||
|
||||
* Add the ablity to set a 'me' contact (only API for now)
|
||||
* Add stepparent/stepchild relationship
|
||||
|
||||
### Enhancements:
|
||||
|
||||
* Update to laravel cashier 10.0, and get ready with SCA/PSD2
|
||||
* Add stripe webhook
|
||||
* Depends on php7.3+
|
||||
* Use pretty-radio and optimize vue.js components
|
||||
|
||||
@@ -7,6 +7,7 @@ use App\Helpers\SearchHelper;
|
||||
use App\Models\Contact\Contact;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Database\QueryException;
|
||||
use App\Services\Contact\Contact\SetMeContact;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
use App\Services\Contact\Contact\CreateContact;
|
||||
use App\Services\Contact\Contact\UpdateContact;
|
||||
@@ -17,6 +18,17 @@ use App\Http\Resources\Contact\ContactWithContactFields as ContactWithContactFie
|
||||
|
||||
class ApiContactController extends ApiController
|
||||
{
|
||||
/**
|
||||
* Instantiate a new controller instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->middleware('limitations')->only('setMe');
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of the contacts.
|
||||
* We will only retrieve the contacts that are "real", not the partials
|
||||
@@ -175,4 +187,25 @@ class ApiContactController extends ApiController
|
||||
|
||||
return ContactResource::collection($contacts);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a contact as 'me'.
|
||||
*
|
||||
* @param Request $request
|
||||
* @param int $contactId
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function setMe(Request $request, $contactId)
|
||||
{
|
||||
$data = [
|
||||
'contact_id' => $contactId,
|
||||
'account_id' => auth()->user()->account->id,
|
||||
'user_id' => auth()->user()->id,
|
||||
];
|
||||
|
||||
app(SetMeContact::class)->execute($data);
|
||||
|
||||
return $this->respond(['true']);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,6 +41,11 @@ class AddressBook extends BaseAddressBook
|
||||
'principal' => '{DAV:}owner',
|
||||
'protected' => true,
|
||||
],
|
||||
[
|
||||
'privilege' => '{DAV:}write-properties',
|
||||
'principal' => '{DAV:}owner',
|
||||
'protected' => true,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ use Sabre\CalDAV\Plugin as CalDAVPlugin;
|
||||
use Sabre\CardDAV\Backend\AbstractBackend;
|
||||
use Sabre\CardDAV\Plugin as CardDAVPlugin;
|
||||
use Sabre\DAV\Sync\Plugin as DAVSyncPlugin;
|
||||
use App\Services\Contact\Contact\SetMeContact;
|
||||
use App\Http\Controllers\DAV\Backend\IDAVBackend;
|
||||
use App\Http\Controllers\DAV\Backend\SyncDAVBackend;
|
||||
use App\Http\Controllers\DAV\DAVACL\PrincipalBackend;
|
||||
@@ -71,6 +72,13 @@ class CardDAVBackend extends AbstractBackend implements SyncSupport, IDAVBackend
|
||||
];
|
||||
}
|
||||
|
||||
$me = auth()->user()->me;
|
||||
if ($me) {
|
||||
$des += [
|
||||
'{'.CalDAVPlugin::NS_CALENDARSERVER.'}me-card' => '/'.config('laravelsabre.path').'/addressbooks/'.Auth::user()->email.'/contacts/'.$this->encodeUri($me),
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
$des,
|
||||
];
|
||||
@@ -373,7 +381,19 @@ class CardDAVBackend extends AbstractBackend implements SyncSupport, IDAVBackend
|
||||
*/
|
||||
public function updateAddressBook($addressBookId, DAV\PropPatch $propPatch)
|
||||
{
|
||||
return false;
|
||||
$propPatch->handle('{'.CalDAVPlugin::NS_CALENDARSERVER.'}me-card', function ($props) {
|
||||
$contact = $this->getObject($props->getHref());
|
||||
|
||||
$data = [
|
||||
'contact_id' => $contact->id,
|
||||
'account_id' => auth()->user()->account->id,
|
||||
'user_id' => auth()->user()->id,
|
||||
];
|
||||
|
||||
app(SetMeContact::class)->execute($data);
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -4,6 +4,7 @@ namespace App\Http\Resources\Account\User;
|
||||
|
||||
use App\Helpers\DateHelper;
|
||||
use Illuminate\Http\Resources\Json\Resource;
|
||||
use App\Http\Resources\Contact\ContactShort as ContactShortResource;
|
||||
use App\Http\Resources\Settings\Currency\Currency as CurrencyResource;
|
||||
|
||||
class User extends Resource
|
||||
@@ -22,6 +23,7 @@ class User extends Resource
|
||||
'first_name' => $this->first_name,
|
||||
'last_name' => $this->last_name,
|
||||
'email' => $this->email,
|
||||
'me_contact' => new ContactShortResource($this->me),
|
||||
'timezone' => $this->timezone,
|
||||
'currency' => new CurrencyResource($this->currency),
|
||||
'locale' => $this->locale,
|
||||
|
||||
@@ -30,6 +30,7 @@ class Contact extends Resource
|
||||
'is_partial' => (bool) $this->is_partial,
|
||||
'is_active' => (bool) $this->is_active,
|
||||
'is_dead' => (bool) $this->is_dead,
|
||||
'is_me' => $this->isMe(),
|
||||
'last_called' => $this->when(! $this->is_partial, $this->getLastCalled()),
|
||||
'last_activity_together' => $this->when(! $this->is_partial, $this->getLastActivityDate()),
|
||||
'stay_in_touch_frequency' => $this->when(! $this->is_partial, $this->stay_in_touch_frequency),
|
||||
|
||||
@@ -27,6 +27,7 @@ class ContactShort extends Contact
|
||||
'gender_type' => is_null($this->gender) ? null : $this->gender->type,
|
||||
'is_partial' => (bool) $this->is_partial,
|
||||
'is_dead' => (bool) $this->is_dead,
|
||||
'is_me' => $this->isMe(),
|
||||
'information' => [
|
||||
'birthdate' => [
|
||||
'is_age_based' => (is_null($this->birthdate) ? null : (bool) $this->birthdate->is_age_based),
|
||||
|
||||
@@ -28,6 +28,7 @@ class ContactWithContactFields extends Contact
|
||||
'is_partial' => (bool) $this->is_partial,
|
||||
'is_active' => (bool) $this->is_active,
|
||||
'is_dead' => (bool) $this->is_dead,
|
||||
'is_me' => $this->isMe(),
|
||||
'last_called' => $this->when(! $this->is_partial, $this->getLastCalled()),
|
||||
'last_activity_together' => $this->when(! $this->is_partial, $this->getLastActivityDate()),
|
||||
'stay_in_touch_frequency' => $this->when(! $this->is_partial, $this->stay_in_touch_frequency),
|
||||
|
||||
@@ -418,6 +418,16 @@ class Contact extends Model
|
||||
return $this->hasMany(Occupation::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if this is the 'me' contact.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isMe()
|
||||
{
|
||||
return $this->id == auth()->user()->me_contact_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort the contacts according a given criteria.
|
||||
* @param Builder $builder
|
||||
|
||||
@@ -8,6 +8,7 @@ use App\Models\Journal\Day;
|
||||
use App\Models\Settings\Term;
|
||||
use App\Helpers\RequestHelper;
|
||||
use App\Models\Account\Account;
|
||||
use App\Models\Contact\Contact;
|
||||
use App\Helpers\CountriesHelper;
|
||||
use App\Models\Settings\Currency;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
@@ -17,6 +18,7 @@ use Illuminate\Support\Facades\App;
|
||||
use Illuminate\Notifications\Notifiable;
|
||||
use Illuminate\Contracts\Auth\MustVerifyEmail;
|
||||
use Illuminate\Validation\UnauthorizedException;
|
||||
use Illuminate\Database\Eloquent\Relations\HasOne;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Foundation\Auth\User as Authenticatable;
|
||||
@@ -177,6 +179,16 @@ class User extends Authenticatable implements MustVerifyEmail
|
||||
return $this->belongsTo(Account::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the contact record associated with the 'me' contact.
|
||||
*
|
||||
* @return HasOne
|
||||
*/
|
||||
public function me()
|
||||
{
|
||||
return $this->hasOne(Contact::class, 'id', 'me_contact_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the term records associated with the user.
|
||||
*
|
||||
|
||||
@@ -87,6 +87,7 @@ class AppServiceProvider extends ServiceProvider
|
||||
\App\Services\Contact\Call\UpdateCall::class => \App\Services\Contact\Call\UpdateCall::class,
|
||||
\App\Services\Contact\Contact\CreateContact::class => \App\Services\Contact\Contact\CreateContact::class,
|
||||
\App\Services\Contact\Contact\DestroyContact::class => \App\Services\Contact\Contact\DestroyContact::class,
|
||||
\App\Services\Contact\Contact\SetMeContact::class => \App\Services\Contact\Contact\SetMeContact::class,
|
||||
\App\Services\Contact\Contact\UpdateBirthdayInformation::class => \App\Services\Contact\Contact\UpdateBirthdayInformation::class,
|
||||
\App\Services\Contact\Contact\UpdateContact::class => \App\Services\Contact\Contact\UpdateContact::class,
|
||||
\App\Services\Contact\Contact\UpdateDeceasedInformation::class => \App\Services\Contact\Contact\UpdateDeceasedInformation::class,
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Contact\Contact;
|
||||
|
||||
use App\Models\User\User;
|
||||
use App\Services\BaseService;
|
||||
use App\Models\Contact\Contact;
|
||||
|
||||
class SetMeContact extends BaseService
|
||||
{
|
||||
/**
|
||||
* Get the validation rules that apply to the service.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'account_id' => 'required|integer|exists:accounts,id',
|
||||
'user_id' => 'required|integer|exists:users,id',
|
||||
'contact_id' => 'required|integer|exists:contacts,id',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a contact as 'me' contact.
|
||||
*
|
||||
* @param array $data
|
||||
* @return User
|
||||
*/
|
||||
public function execute(array $data) : User
|
||||
{
|
||||
$this->validate($data);
|
||||
|
||||
$user = User::where('account_id', $data['account_id'])
|
||||
->findOrFail($data['user_id']);
|
||||
|
||||
if ($user->account->hasLimitations()) {
|
||||
abort(402);
|
||||
}
|
||||
|
||||
$contact = Contact::where('account_id', $data['account_id'])
|
||||
->findOrFail($data['contact_id']);
|
||||
|
||||
$user->me_contact_id = $contact->id;
|
||||
$user->save();
|
||||
|
||||
return $user;
|
||||
}
|
||||
}
|
||||
@@ -72,6 +72,7 @@ $db = [
|
||||
'driver' => 'sqlite',
|
||||
'database' => env('DB_DATABASE', database_path('database.sqlite')),
|
||||
'prefix' => env('DB_PREFIX', ''),
|
||||
'foreign_key_constraints' => true,
|
||||
],
|
||||
|
||||
'mysql' => [
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class AddMeContactOnUser extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->unsignedInteger('me_contact_id')->after('email')->nullable();
|
||||
$table->foreign('me_contact_id')->references('id')->on('contacts')->onDelete('set null');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->dropForeign(['me_contact_id']);
|
||||
$table->dropColumn('me_contact_id');
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -11,8 +11,11 @@
|
||||
|
||||
# Redirect .well-known urls (https://en.wikipedia.org/wiki/List_of_/.well-known/_services_offered_by_webservers)
|
||||
RewriteCond %{REQUEST_URI} .well-known/carddav
|
||||
RewriteRule ^ /dav/ [L,R=301]
|
||||
|
||||
RewriteCond %{REQUEST_URI} .well-known/caldav
|
||||
RewriteRule ^ /dav/ [L,R=301]
|
||||
|
||||
RewriteCond %{REQUEST_URI} .well-known/security.txt
|
||||
RewriteRule ^ /security.txt [L,R=301]
|
||||
# old carddav url
|
||||
|
||||
File diff suppressed because one or more lines are too long
Vendored
+1
-1
File diff suppressed because one or more lines are too long
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"/js/manifest.js": "/js/manifest.js?id=01c8731923a46c30aaed",
|
||||
"/js/vendor.js": "/js/vendor.js?id=46a1680b35e3530942a8",
|
||||
"/js/vendor.js": "/js/vendor.js?id=4638be4cb4d81526ae20",
|
||||
"/js/app.js": "/js/app.js?id=807ac6ae4faa6baf4790",
|
||||
"/css/app-ltr.css": "/css/app-ltr.css?id=d1bc921f46ec60c3bbca",
|
||||
"/css/app-rtl.css": "/css/app-rtl.css?id=72920cbd345b2fe95fd4",
|
||||
|
||||
@@ -64,6 +64,7 @@ return [
|
||||
'list_link_to_archived_contacts' => 'List of archived contacts',
|
||||
|
||||
// Header
|
||||
'me' => 'This is you',
|
||||
'edit_contact_information' => 'Edit contact information',
|
||||
'contact_archive' => 'Archive contact',
|
||||
'contact_unarchive' => 'Unarchive contact',
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
<div class="ph3 ph5-ns pv2 cf w-100 mt4 mt0-ns">
|
||||
<div class="alert alert-success tc">
|
||||
{{ trans('people.me') }}
|
||||
</div>
|
||||
<div class="mw9 center dt w-100 box-shadow pa4 relative">
|
||||
|
||||
{{-- AVATAR --}}
|
||||
@@ -33,6 +36,7 @@
|
||||
</li>
|
||||
|
||||
{{-- LAST ACTIVITY --}}
|
||||
@if (! $contact->isMe())
|
||||
<li class="mb2 mb0-ns dn di-ns tc {{ htmldir() == 'ltr' ? 'mr3-ns' : 'ml3-ns' }}">
|
||||
<span class="{{ htmldir() == 'ltr' ? 'mr1' : 'ml1' }}">@include('partials.icons.header_people')</span>
|
||||
@if (is_null($contact->getLastActivityDate()))
|
||||
@@ -41,8 +45,10 @@
|
||||
{{ trans('people.last_activity_date', ['date' => \App\Helpers\DateHelper::getShortDate($contact->getLastActivityDate())]) }}
|
||||
@endif
|
||||
</li>
|
||||
@endif
|
||||
|
||||
{{-- LAST CALLED --}}
|
||||
@if (! $contact->isMe())
|
||||
<li class="mb2 mb0-ns dn di-ns tc {{ htmldir() == 'ltr' ? 'mr3-ns' : 'ml3-ns' }}">
|
||||
<span class="{{ htmldir() == 'ltr' ? 'mr1' : 'ml1' }}">@include('partials.icons.header_call')</span>
|
||||
@if (is_null($contact->getLastCalled()))
|
||||
@@ -51,6 +57,7 @@
|
||||
{{ trans('people.last_called', ['date' => \App\Helpers\DateHelper::getShortDate($contact->getLastCalled())]) }}
|
||||
@endif
|
||||
</li>
|
||||
@endif
|
||||
|
||||
{{-- DESCRIPTION --}}
|
||||
@if ($contact->description)
|
||||
|
||||
@@ -81,22 +81,26 @@
|
||||
|
||||
<div class="flex items-center justify-center flex-column">
|
||||
<div class='cf dib'>
|
||||
@if (! $contact->isMe())
|
||||
<span @click="updateDefaultProfileView('life-events')" :class="[global_profile_default_view == 'life-events' ? 'f6 fl bb bt br bl ph3 pv2 dib b br2 br--left bl mb4 b--gray-monica' : 'f6 fl bb bt br ph3 pv2 dib bg-gray-monica br2 br--left bl pointer mb4 b--gray-monica']">
|
||||
@if (auth()->user()->profile_new_life_event_badge_seen == false)
|
||||
<span class="bg-light-green f7 mr2 ph2 pv1 br2">{{ trans('app.new') }}</span>
|
||||
@endif
|
||||
{{ trans('people.life_event_list_tab_life_events') }} ({{ $contact->lifeEvents()->count() }})
|
||||
</span>
|
||||
@endif
|
||||
<span @click="updateDefaultProfileView('notes')" :class="[global_profile_default_view == 'notes' ? 'f6 fl bb bt ph3 pv2 dib b br--right br mb4 b--gray-monica' : 'f6 fl bb bt ph3 pv2 dib bg-gray-monica br--right br pointer mb4 b--gray-monica']">{{ trans('people.life_event_list_tab_other') }}</span>
|
||||
<span @click="updateDefaultProfileView('photos')" :class="[global_profile_default_view == 'photos' ? 'f6 fl bb bt ph3 pv2 dib b br2 br--right br mb4 b--gray-monica' : 'f6 fl bb bt ph3 pv2 dib bg-gray-monica br2 br--right br pointer mb4 b--gray-monica']">Photos</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if (! $contact->isMe())
|
||||
<div v-if="global_profile_default_view == 'life-events'">
|
||||
<div class="row section">
|
||||
@include('people.life-events.index')
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div v-if="global_profile_default_view == 'notes'">
|
||||
@if ($modules->contains('key', 'notes'))
|
||||
@@ -107,19 +111,19 @@
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if ($modules->contains('key', 'conversations'))
|
||||
@if ($modules->contains('key', 'conversations') && ! $contact->isMe())
|
||||
<div class="row section">
|
||||
@include('people.conversations.index')
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if ($modules->contains('key', 'phone_calls'))
|
||||
@if ($modules->contains('key', 'phone_calls') && ! $contact->isMe())
|
||||
<div class="row section calls">
|
||||
@include('people.calls.index')
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if ($modules->contains('key', 'activities'))
|
||||
@if ($modules->contains('key', 'activities') && ! $contact->isMe())
|
||||
<div class="row section activities">
|
||||
@include('activities.index')
|
||||
</div>
|
||||
@@ -137,13 +141,13 @@
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if ($modules->contains('key', 'gifts'))
|
||||
@if ($modules->contains('key', 'gifts') && ! $contact->isMe())
|
||||
<div class="row section">
|
||||
@include('people.gifts.index')
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if ($modules->contains('key', 'debts'))
|
||||
@if ($modules->contains('key', 'debts') && ! $contact->isMe())
|
||||
<div class="row section debts">
|
||||
@include('people.debt.index')
|
||||
</div>
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
@endif
|
||||
|
||||
{{-- Introductions --}}
|
||||
@if ($modules->contains('key', 'how_you_met'))
|
||||
@if ($modules->contains('key', 'how_you_met') && ! $contact->isMe())
|
||||
@include('people.introductions.index')
|
||||
@endif
|
||||
|
||||
@@ -24,6 +24,6 @@
|
||||
@endif
|
||||
|
||||
{{-- Food preferences --}}
|
||||
@if ($modules->contains('key', 'food_preferences'))
|
||||
@if ($modules->contains('key', 'food_preferences') && ! $contact->isMe())
|
||||
@include('people.food-preferences.index')
|
||||
@endif
|
||||
|
||||
@@ -19,6 +19,7 @@ Route::group(['middleware' => ['auth:api']], function () {
|
||||
|
||||
// Contacts
|
||||
Route::apiResource('contacts', 'ApiContactController');
|
||||
Route::put('/contacts/{contact}/setMe', 'ApiContactController@setMe');
|
||||
|
||||
// Genders
|
||||
Route::apiResource('genders', 'Account\\ApiGenderController');
|
||||
|
||||
@@ -27,6 +27,7 @@ class ApiContactControllerTest extends ApiTestCase
|
||||
'is_starred',
|
||||
'is_partial',
|
||||
'is_dead',
|
||||
'is_me',
|
||||
'last_called',
|
||||
'last_activity_together',
|
||||
'stay_in_touch_frequency',
|
||||
@@ -1352,4 +1353,44 @@ class ApiContactControllerTest extends ApiTestCase
|
||||
'id' => $contact->id,
|
||||
]);
|
||||
}
|
||||
|
||||
public function test_it_sets_me_contact()
|
||||
{
|
||||
$user = $this->signin();
|
||||
$contact = factory(Contact::class)->create([
|
||||
'account_id' => $user->account_id,
|
||||
]);
|
||||
|
||||
$response = $this->json('PUT', '/api/contacts/'.$contact->id.'/setMe');
|
||||
|
||||
$response->assertStatus(200);
|
||||
|
||||
$this->assertDatabaseHas('users', [
|
||||
'account_id' => $user->account_id,
|
||||
'me_contact_id' => $contact->id,
|
||||
]);
|
||||
}
|
||||
|
||||
public function test_it_gets_me_contact()
|
||||
{
|
||||
$user = $this->signin();
|
||||
$contact = factory(Contact::class)->create([
|
||||
'account_id' => $user->account_id,
|
||||
]);
|
||||
$user->me_contact_id = $contact->id;
|
||||
$user->save();
|
||||
|
||||
$response = $this->json('GET', '/api/contacts/'.$contact->id);
|
||||
|
||||
$response->assertOk();
|
||||
|
||||
$response->assertJsonStructure([
|
||||
'data' => $this->jsonStructureContactShort,
|
||||
]);
|
||||
|
||||
$response->assertJsonFragment([
|
||||
'id' => $contact->id,
|
||||
'is_me' => true,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -177,6 +177,82 @@ class CardDAVTest extends ApiTestCase
|
||||
);
|
||||
}
|
||||
|
||||
public function test_carddav_get_me_card()
|
||||
{
|
||||
$user = $this->signin();
|
||||
$contact = factory(Contact::class)->create([
|
||||
'account_id' => $user->account->id,
|
||||
]);
|
||||
$user->me_contact_id = $contact->id;
|
||||
$user->save();
|
||||
|
||||
$response = $this->call('PROPFIND', "/dav/addressbooks/{$user->email}", [], [], [],
|
||||
[
|
||||
'HTTP_DEPTH' => '1',
|
||||
'content-type' => 'application/xml; charset=utf-8',
|
||||
],
|
||||
"<propfind xmlns='DAV:' xmlns:cs='http://calendarserver.org/ns/'>
|
||||
<prop>
|
||||
<cs:me-card />
|
||||
</prop>
|
||||
</propfind>"
|
||||
);
|
||||
|
||||
$response->assertStatus(207);
|
||||
$response->assertHeader('X-Sabre-Version');
|
||||
|
||||
$response->assertSee('<d:response>'.
|
||||
"<d:href>/dav/addressbooks/{$user->email}/contacts/</d:href>".
|
||||
'<d:propstat>'.
|
||||
'<d:prop>'.
|
||||
"<cs:me-card>/dav/addressbooks/{$user->email}/contacts/{$contact->uuid}.vcf</cs:me-card>".
|
||||
'</d:prop>'.
|
||||
'<d:status>HTTP/1.1 200 OK</d:status>'.
|
||||
'</d:propstat>'.
|
||||
'</d:response>'
|
||||
);
|
||||
}
|
||||
|
||||
public function test_carddav_set_me_card()
|
||||
{
|
||||
$user = $this->signin();
|
||||
$contact = factory(Contact::class)->create([
|
||||
'account_id' => $user->account->id,
|
||||
]);
|
||||
|
||||
$response = $this->call('PROPPATCH', "/dav/addressbooks/{$user->email}/contacts", [], [], [],
|
||||
[
|
||||
'content-type' => 'application/xml; charset=utf-8',
|
||||
],
|
||||
"<propertyupdate xmlns='DAV:' xmlns:cs='http://calendarserver.org/ns/'>
|
||||
<set>
|
||||
<prop>
|
||||
<cs:me-card>
|
||||
<href>/dav/addressbooks/{$user->email}/contacts/{$contact->uuid}.vcf</href>
|
||||
</cs:me-card>
|
||||
</prop>
|
||||
</set>
|
||||
</propertyupdate>"
|
||||
);
|
||||
|
||||
$response->assertSee('<d:multistatus xmlns:d="DAV:" xmlns:s="http://sabredav.org/ns" xmlns:card="urn:ietf:params:xml:ns:carddav" xmlns:cal="urn:ietf:params:xml:ns:caldav" xmlns:cs="http://calendarserver.org/ns/">');
|
||||
$response->assertSee('<d:response>'.
|
||||
"<d:href>/dav/addressbooks/{$user->email}/contacts</d:href>".
|
||||
'<d:propstat>'.
|
||||
'<d:prop>'.
|
||||
'<cs:me-card/>'.
|
||||
'</d:prop>'.
|
||||
'<d:status>HTTP/1.1 200 OK</d:status>'.
|
||||
'</d:propstat>'.
|
||||
'</d:response>'
|
||||
);
|
||||
|
||||
$this->assertDatabaseHas('users', [
|
||||
'id' => $user->id,
|
||||
'me_contact_id' => $contact->id,
|
||||
]);
|
||||
}
|
||||
|
||||
public function test_carddav_getctag_contacts()
|
||||
{
|
||||
$user = $this->signin();
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Unit\Services\Contact\Contact;
|
||||
|
||||
use Tests\TestCase;
|
||||
use App\Models\User\User;
|
||||
use App\Models\Contact\Contact;
|
||||
use App\Services\Contact\Contact\SetMeContact;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
||||
use Illuminate\Database\Eloquent\ModelNotFoundException;
|
||||
|
||||
class SetMeContactTest extends TestCase
|
||||
{
|
||||
use DatabaseTransactions;
|
||||
|
||||
public function test_it_set_me_as_a_a_contact()
|
||||
{
|
||||
$user = factory(User::class)->create();
|
||||
$contact = factory(Contact::class)->create([
|
||||
'account_id' => $user->account->id,
|
||||
]);
|
||||
|
||||
$request = [
|
||||
'account_id' => $user->account->id,
|
||||
'user_id' => $user->id,
|
||||
'contact_id' => $contact->id,
|
||||
];
|
||||
|
||||
$user = app(SetMeContact::class)->execute($request);
|
||||
|
||||
$this->assertDatabaseHas('users', [
|
||||
'id' => $user->id,
|
||||
'account_id' => $user->account->id,
|
||||
'me_contact_id' => $contact->id,
|
||||
]);
|
||||
}
|
||||
|
||||
public function test_it_fails_if_wrong_parameters_are_given()
|
||||
{
|
||||
$user = factory(User::class)->create();
|
||||
$contact = factory(Contact::class)->create([
|
||||
'account_id' => $user->account->id,
|
||||
]);
|
||||
|
||||
$request = [
|
||||
'account_id' => $user->account->id,
|
||||
'user_id' => $user->id,
|
||||
'contact_id' => 0,
|
||||
];
|
||||
|
||||
$this->expectException(ValidationException::class);
|
||||
app(SetMeContact::class)->execute($request);
|
||||
}
|
||||
|
||||
public function test_it_throws_an_exception_if_contact_not_found()
|
||||
{
|
||||
$user = factory(User::class)->create();
|
||||
$contact = factory(Contact::class)->create();
|
||||
|
||||
$request = [
|
||||
'account_id' => $user->account->id,
|
||||
'user_id' => $user->id,
|
||||
'contact_id' => $contact->id,
|
||||
];
|
||||
|
||||
$this->expectException(ModelNotFoundException::class);
|
||||
app(SetMeContact::class)->execute($request);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user