refactor: replace custom CRM with Monica fork
Some checks failed
Build & Push Monica Image to Gitea Registry / build-and-push (push) Failing after 9s

This commit is contained in:
Kroonk
2026-05-22 16:19:55 +02:00
parent 38cc4c09ca
commit a213a1dba0
2215 changed files with 242567 additions and 6015 deletions

View File

@@ -0,0 +1,105 @@
<?php
namespace App\Services\VCalendar;
use Illuminate\Support\Str;
use App\Models\Contact\Task;
use App\Services\BaseService;
use Sabre\VObject\Component\VTodo;
use Illuminate\Support\Facades\Auth;
use Sabre\VObject\Component\VCalendar;
class ExportTask extends BaseService
{
/**
* Get the validation rules that apply to the service.
*
* @return array
*/
public function rules()
{
return [
'account_id' => 'required|integer|exists:accounts,id',
'task_id' => 'required|integer|exists:tasks,id',
];
}
/**
* Export one VCalendar.
*
* @param array $data
* @return VCalendar
*/
public function execute(array $data): VCalendar
{
$this->validate($data);
$task = Task::where('account_id', $data['account_id'])
->findOrFail($data['task_id']);
return $this->export($task);
}
/**
* @param Task $task
* @return VCalendar
*/
private function export(Task $task): VCalendar
{
// The standard for most of these fields can be found on https://datatracker.ietf.org/doc/html/rfc5545
if (! $task->uuid) {
$task->forceFill([
'uuid' => Str::uuid(),
])->save();
}
// Basic information
$vcal = new VCalendar();
$vtodo = $vcal->create('VTODO');
$vcal->add($vtodo);
$this->exportTimezone($vcal);
$this->exportVTodo($task, $vtodo);
return $vcal;
}
/**
* @param VCalendar $vcal
*/
private function exportTimezone(VCalendar $vcal)
{
$vcal->add('VTIMEZONE', [
'TZID' => Auth::user()->timezone,
]);
}
/**
* @param Task $task
* @param VTodo $vtodo
*/
private function exportVTodo(Task $task, VTodo $vtodo)
{
$contact = $task->contact;
$vtodo->UID = $task->uuid;
$vtodo->SUMMARY = $task->title;
if ($task->created_at) {
$vtodo->DTSTAMP = $task->created_at;
$vtodo->CREATED = $task->created_at;
}
if (! empty($task->description)) {
$vtodo->DESCRIPTION = $task->description;
}
if ($contact) {
$vtodo->ATTACH = $contact->getLink();
}
if ($task->completed) {
$vtodo->STATUS = 'COMPLETED';
}
if ($task->completed_at) {
$vtodo->COMPLETED = $task->completed_at;
}
}
}

View File

@@ -0,0 +1,108 @@
<?php
namespace App\Services\VCalendar;
use Illuminate\Support\Str;
use App\Services\BaseService;
use Sabre\VObject\Component\VEvent;
use App\Models\Instance\SpecialDate;
use Illuminate\Support\Facades\Auth;
use Sabre\VObject\Component\VCalendar;
class ExportVCalendar extends BaseService
{
/**
* Get the validation rules that apply to the service.
*
* @return array
*/
public function rules()
{
return [
'account_id' => 'required|integer|exists:accounts,id',
'special_date_id' => 'required|integer|exists:special_dates,id',
];
}
/**
* Export one VCalendar.
*
* @param array $data
* @return VCalendar
*/
public function execute(array $data): VCalendar
{
$this->validate($data);
$date = SpecialDate::where('account_id', $data['account_id'])
->findOrFail($data['special_date_id']);
return $this->export($date);
}
/**
* @param SpecialDate $date
* @return VCalendar
*/
private function export(SpecialDate $date): VCalendar
{
// The standard for most of these fields can be found on https://datatracker.ietf.org/doc/html/rfc5545
if (! $date->uuid) {
$date->forceFill([
'uuid' => Str::uuid(),
])->save();
}
// Basic information
$vcal = new VCalendar();
$vevent = $vcal->create('VEVENT');
$vcal->add($vevent);
$this->exportTimezone($vcal);
$this->exportBirthday($date, $vevent);
return $vcal;
}
/**
* @param VCalendar $vcal
* @return void
*/
private function exportTimezone(VCalendar $vcal)
{
$vcal->add('VTIMEZONE', [
'TZID' => Auth::user()->timezone,
]);
}
/**
* @param SpecialDate $date
* @param VEvent $vevent
* @return void
*/
private function exportBirthday(SpecialDate $date, VEvent $vevent)
{
$contact = $date->contact;
$vevent->UID = $date->uuid;
$vevent->DTSTART = $date->date->format('Ymd');
$vevent->DTSTART['VALUE'] = 'DATE';
$vevent->DTEND = $date->date->addDays(1)->format('Ymd');
$vevent->DTEND['VALUE'] = 'DATE';
$vevent->RRULE = 'FREQ=YEARLY';
if ($date->created_at) {
$vevent->DTSTAMP = $date->created_at;
$vevent->CREATED = $date->created_at;
}
if ($contact) {
$name = $contact->name;
$vevent->SUMMARY = trans('people.reminders_birthday', ['name' => $name]);
$vevent->ATTACH = $contact->getLink();
$vevent->DESCRIPTION = trans('mail.footer_contact_info2_link', [
'name' => $name,
'url' => $contact->getLink(),
]);
}
}
}

View File

@@ -0,0 +1,189 @@
<?php
namespace App\Services\VCalendar;
use Ramsey\Uuid\Uuid;
use App\Traits\DAVFormat;
use Sabre\VObject\Reader;
use Illuminate\Support\Arr;
use App\Models\Contact\Task;
use App\Services\BaseService;
use Illuminate\Support\Carbon;
use Sabre\VObject\ParseException;
use Sabre\VObject\Component\VCalendar;
class ImportTask extends BaseService
{
use DAVFormat;
/**
* Get the validation rules that apply to the service.
*
* @return array
*/
public function rules()
{
return [
'account_id' => 'required|integer|exists:accounts,id',
'task_id' => 'nullable|integer|exists:tasks,id',
'entry' => 'required|string',
];
}
/**
* Export one VCalendar.
*
* @param array $data
* @return array
*/
public function execute(array $data): array
{
$this->validate($data);
if (Arr::has($data, 'task_id') && ! is_null($data['task_id'])) {
$task = Task::where('account_id', $data['account_id'])
->findOrFail($data['task_id']);
} else {
$task = new Task(['account_id' => $data['account_id']]);
}
return $this->process($data, $task);
}
/**
* Import one VCalendar.
*
* @param array $data
* @param Task $task
* @return array
*/
private function process(array $data, Task $task): array
{
$entry = $this->getEntry($data);
if (! $entry) {
return [
'error' => '0',
];
}
if (! $this->canImportCurrentEntry($entry)) {
return [
'error' => '1',
];
}
$task = $this->importEntry($task, $entry);
return [
'task_id' => $task->id,
];
}
/**
* Check whether this entry contains a VTODO. If not, it
* can not be imported.
*
* @param VCalendar $entry
* @return bool
*/
private function canImportCurrentEntry(VCalendar $entry): bool
{
return ! is_null($entry->VTODO);
}
/**
* Create the Task object matching the current entry.
*
* @param Task $task
* @param VCalendar $entry
* @return Task
*/
private function importEntry($task, VCalendar $entry): Task
{
$this->importUid($task, $entry);
$this->importSummary($task, $entry);
$this->importCompleted($task, $entry);
$this->importTimestamp($task, $entry);
$task->save();
return $task;
}
/**
* @param array $data
* @return VCalendar|null
*/
private function getEntry($data): ?VCalendar
{
try {
$entry = Reader::read($data['entry'], Reader::OPTION_FORGIVING + Reader::OPTION_IGNORE_INVALID_LINES);
if ($entry instanceof VCalendar) {
return $entry;
}
} catch (ParseException $e) {
// catch parse errors
}
return null;
}
/**
* Import uid.
*
* @param Task $task
* @param VCalendar $entry
* @return void
*/
private function importUid(Task $task, VCalendar $entry): void
{
if (empty($task->uuid) && Uuid::isValid((string) $entry->VTODO->UID)) {
$task->uuid = (string) $entry->VTODO->UID;
}
}
/**
* Import uid.
*
* @param Task $task
* @param VCalendar $entry
* @return void
*/
private function importTimestamp(Task $task, VCalendar $entry): void
{
if (empty($task->created_at)) {
if ($entry->VTODO->DTSTAMP) {
$task->created_at = Carbon::parse($entry->VTODO->DTSTAMP->getDateTime());
} elseif ($entry->VTODO->CREATED) {
$task->created_at = Carbon::parse($entry->VTODO->CREATED->getDateTime());
}
}
}
/**
* @param Task $task
* @param VCalendar $entry
*/
private function importSummary(Task $task, VCalendar $entry)
{
$task->title = $this->formatValue($entry->VTODO->SUMMARY);
if ($entry->VTODO->DESCRIPTION) {
$task->description = $this->formatValue($entry->VTODO->DESCRIPTION);
}
}
/**
* @param Task $task
* @param VCalendar $entry
*/
private function importCompleted(Task $task, VCalendar $entry)
{
$task->completed = ((string) $entry->VTODO->STATUS) == 'COMPLETED';
if (! $task->completed) {
$task->completed_at = null;
} elseif ($entry->VTODO->COMPLETED) {
$task->completed_at = Carbon::parse($entry->VTODO->COMPLETED->getDateTime());
}
}
}