Aller au contenu

omnibase/office Référence de l’API

Étage
Structure préparée · Omnibase
Version
main
Vérifié en réel
Son README ne le dit pas
Licence
MIT
composer require omnibase/office:dev-main

Lue dans les fichiers PHP du paquet au commit ee6b1b7f36c6 : ses espaces de noms, classes, interfaces et énumérations, leurs constantes, propriétés et méthodes publiques avec leur PHPDoc, et les options que prennent ses fabriques. Rien n’en est exécuté.

Base\Office

  • OfficeBundle class

    A professional practice on its own site, whatever the profession: the office (address, access, team, hours, contact - the core), appointments by slot or by request (Booking), an encrypted document vault per recipient with its access log (Share), and a video room with a waiting room (Visio), its calls carried by a gateway of glitchr/omnimeet - from browser to browser by default. Nothing here knows health, law or payment: a regime (omnibase/health, later omnibase/notary, omnibase/lawyer) puts its trade on top - its templates, its compliance checks, who may read a document.

    3 methods

Base\Office\Admin\Settings

  • OfficeLegalSection class

    The settings' "Office" section: what the legal notice must say and only the office knows - who hosts the site, who to write to about one's data.

    3 methods

Base\Office\Admin\Widget

  • ComplianceWidgetType class

    The dashboard's "Compliance": the office's checks and its regime's, each with what to do when it is not met.

    4 methods

  • ContactRequestsWidgetType class

    The dashboard's "Messages from the site": the contact requests not handled yet.

    4 methods

  • TodayWidgetType class

    The dashboard's "Today": the day's appointments, of the member signed in or of everyone, and the requests waiting for a time.

    4 methods

  • WaitingRoomWidgetType class

    The dashboard's "Waiting room": video rooms whose guest is there and whose host is not yet.

    4 methods

Base\Office\Booking

  • AppointmentRequests class

    A first appointment asked without an account: a practice's clients are invited, so someone new cannot go through the booking pages, which ask to sign in. The request becomes an appointment in request mode (no time yet: the practice fixes it) under the name, the e-mail and the phone given; the few words are kept encrypted, as every reason is.

    5 methods

  • BeneficiaryProviderInterface interface

    Who else a client may book for: health's dependants (a child, a parent cared for). Each choice has a label - what the appointment shows - and what the appointment keeps in its meta.

    1 method

  • Booker class

    Booking, moving, cancelling. A slot is taken under a lock on the member's day, after asking the SlotFinder again; the unique slot key is the last word if two servers race. The client's reason is sealed by the Cipher before it is stored.

    8 methods

  • BookingMailer class

    The client's e-mails: booked (with the .ics and the Google Calendar link), request received, confirmed, moved, cancelled, reminded. They say when, with whom, where and how to cancel - never why: the reason and the type's name stay in the site.

    7 methods

  • BookingPolicy class

    Whether a client may book this type with this member online: open to everyone, to the clients the member has already seen, or to the staff alone (then by phone).

    3 methods

  • CalendarEntries class

    An appointment as omnibase's calendar bricks read it (Base\Service\ Calendar\Ics, GoogleCalendarLink): "Rendez-vous - <office>", with whom, where. Neither the reason nor the type's name: a calendar is synced to phones and to Google, it is not a place for them.

    2 methods

  • SlotFinder class

    The free slots of one member for one appointment type: the weekly schedules, less the member's absences, less the days the office is closed (omnibase's special days), less the appointments already held and the buffer after each, less what is too soon (min_notice) or too far (horizon) - those two only for clients, not for the staff.

    6 methods

Base\Office\Command

  • CloseDayCommand class

    The day closed, from the cron container each night: a confirmed appointment now past is DONE - a video one whose guest never came into the room is a NO_SHOW (in person, the staff marks it) - and the closed rooms' handshakes are purged.

    1 method

  • RemindCommand class

    The reminders, from the cron container every quarter of an hour: each confirmed appointment gets one reminder per office.booking.reminders entry (24 and 2 hours before by default), once.

    1 method

Base\Office\Compliance

  • Compliance class

    Every check, run: the widget's list.

    2 methods

  • ComplianceCheckInterface interface

    One thing the back office's "Compliance" widget checks: the vault's key is set, the legal notice names the host, the fees are displayed... The office has its own; each regime adds its trade's (autoconfigured, tag office.compliance_check).

    1 method

  • ComplianceResult class

    3 methods · 5 properties · 3 constants

  • LegalNoticeCheck class

    The legal notice names the host (and, in the settings, who to write to about one's data).

    2 methods

  • TurnCheck class

    The video's relay: configured, and answering on its first address (a UDP STUN binding request - the same port coturn listens on for TURN). It is the direct gateway's (glitchr/omnimeet, omnimeet/direct): a practice whose calls go through another gateway has no relay of its own to check.

    3 methods

  • VaultKeyCheck class

    2 methods

Base\Office\Controller\Admin

  • AdminPageTrait trait

    A page of the bundle's in the back office's chrome (it extends @Admin/layout.html.twig): its menus built when no CRUD did.

    1 method

  • AgendaController class

    The agenda, in the back office: a day (every member a column) or a week (one member, a day a column), drawn by hand on a quarter-hour grid; the quick booking the secretariat takes on the phone - an existing client or a new one, who is then invited to open their account; done, no-show, cancel, move. A member sees their own columns' reasons; the others see that the time is taken.

    4 methods · 2 constants

  • DocumentsController class

    The vault from the office's side: sending a document to a client (who is mailed "a document awaits", nothing more), the list of what was sent - a title is shown only to whom the DocumentVoter lets see it -, and the access log for the coordination.

    5 methods

  • OpenToTrait trait

    The office's screens are written by the office - its coordination (ROLE_ADMIN) or its staff (ROLE_STAFF: the agenda's screens) - not only by the super-admin omnibase/admin asks by default for anything that writes.

Base\Office\Controller\Admin\Crud

  • AbsenceCrudController class

    A member away: their slots are gone for that time.

    6 methods

  • AppointmentCrudController class

    Every appointment, as a list (the agenda is the screen to work in): who, when, its status. The client's reason is not shown here.

    6 methods

  • AppointmentTypeCrudController class

    What an appointment is for: duration, buffer, channel, who may book it online, by slot or by request.

    6 methods

  • ContactRequestCrudController class

    The contact form's messages, read and marked handled; purged after office.contact.retention_months.

    6 methods

  • MemberCrudController class

    The team as the site shows it. "Read the register" asks the member's professional register again (glitchr/omnistate) and keeps what it said.

    8 methods

  • OfficeCrudController class

    The office's places: address, phones, access, accessibility. The opening hours are omnibase's (the back office's hours screen).

    6 methods

  • ScheduleCrudController class

    The weekly schedules: who receives when, where, for which types.

    6 methods

  • ServiceAreaCrudController class

    Where the office goes to its clients: postcodes, towns, or a radius.

    5 methods

  • WalkInCrudController class

    Hours without an appointment: the blood tests at 7:30, a walk-in morning.

    5 methods

Base\Office\Controller\Client

  • AbstractRequestController class

    The page where a first appointment is asked without an account (Base\Office\Booking\AppointmentRequests), for the bundle's own route and for a regime's: the regime routes an action of its own and answers with respond(), naming its template, the translation domain of its error and the prefix of its labels.

    1 method

  • BookingController class

    Booking online, one step a page so that it works on a phone and without scripts: who → what for → when → (signed in) confirm → done. A type in request mode asks instead of picking a slot. Cancelling needs the link of the e-mail, or the client's space.

    10 methods

  • InvitationController class

    The invitation's link: choose an identifier and a password, the account is opened.

    1 method

  • OfficeController class

    The office's public pages: the team, a member's page, the hours and how to come, the contact form. Each template can be the regime's own (office.templates.*): health shows practitioners with their fees.

    5 methods

  • RequestController class

    The bundle's own page for a first appointment asked without an account, for a practice whose regime has none of its own. Off by default (office.booking.public_request: true opens it): the booking pages ask to sign in, and whether someone unknown may ask is the practice's choice.

    2 methods

  • ShareController class

    The client's documents: the list, a download, a deposit. A file only ever leaves through here: signed in, allowed by the DocumentVoter, written in the access log, sent with no-store, nosniff and as an attachment (an image may be shown inline, sandboxed, in a conversation).

    5 methods

  • VisioController class

    The video room: its page (camera check, waiting room, the call), and the small JSON endpoints its script asks - the ICE servers with fresh TURN credentials, the handshake (post, poll), "I am here", a file dropped during the call (kept in the vault for the guest). Only the room's two participants get anything, and only while it is open.

    8 methods

Base\Office\DependencyInjection

  • OfficeConfiguration class

    1 method

  • OfficeExtension class

    4 methods · 1 constant

  • Vocabulary class

    How the practice is named in the bundle's texts: "le cabinet" for a law firm, "l'étude" or "l'office" for a notary's, "la maison de santé"... office.vocabulary.practice names a preset or gives the forms; they reach every translation as global parameters (framework.translator.globals):

    1 method · 1 constant

Base\Office\DependencyInjection\Compiler

  • VisioGatewayPass class

    An application that has not registered glitchr/omnimeet's bundle (Omnimeet\Bridge\Symfony\OmnimeetBundle) still has its video room: the registry is made here with the direct gateway alone, its relay read from office.visio's own keys - what the bundle did before the family existed.

    1 method

Base\Office\Entity

  • Area class

    A field of practice (family, real estate, labour...): what it covers, what is done in it, who of the team follows it. Information for the public, whose wording a regime checks (Base\Office\Guard\Wording).

    24 methods

  • ContactRequest class

    A message from the contact form, kept for the back office as long as the form's notice says (office.contact.retention_months), then purged.

    11 methods

  • Invitation class

    A client invited to open their account (omnibase's invitation by token): the secretary took an appointment on the phone for someone new - nobody signs up by themselves into an office's client space.

    3 methods

  • Member class

    Someone of the team, as the site presents them: a portrait, a title, the languages they speak, a biography - and, for a regulated profession, their number in its register (registryId: an RPPS number, a bar number), read again by Base\Office\Service\MemberRegistry. The account they sign in with is optional: a retired partner or a replacement may be on the page without one. Appointments are booked with a Member.

    38 methods

  • Office class

    A place where the practice receives: its address, how to reach it, what it offers to someone in a wheelchair, its phones and languages. Most practices have one; a second site, a branch, is a second Office. The opening hours are omnibase's (Base\Service\OpeningHours), not a copy.

    45 methods

  • WalkIn class

    Hours when the practice receives without an appointment: the nurses' blood tests from 7:30 to 8:30, a walk-in consultation on Saturday morning. Shown with the opening hours; not bookable.

    20 methods

Base\Office\Entity\Booking

  • Absence class

    A member away: holidays, a training, an afternoon off. Their slots are gone for that time. The whole office closed is a special day of omnibase's opening hours (Base\Entity\Hours\SpecialDay), not an absence.

    12 methods

  • Appointment class

    An appointment with a member: when (or not yet, for a request), what for, where - an office, an address for a home visit, a video room - and who for: the client's account, or a name taken on the phone, perhaps a relative (a child, a parent cared for: `beneficiary`). The reason the client typed is kept encrypted (reasonCipher: Base\Office\Share\Cipher); no e-mail ever carries it.

    60 methods

  • AppointmentType class

    What an appointment is for, as the client chooses it: "Consultation", "Téléconsultation", "Prise de sang à domicile", "Signature d'acte". Its duration and the buffer after it, how it takes place, who may book it online, whether the office confirms it by hand, what to bring - and its mode: a free slot picked by the client, or a request the office answers.

    41 methods

  • Schedule class

    When a member receives, week after week: Monday 8:30-12:00 at the main office, for these appointment types (none listed: all of theirs), valid from - until (a summer timetable, a replacement). No office: the time is for home visits or remote appointments.

    22 methods

  • ServiceArea class

    Where the office goes to its clients: a list of postcodes and towns, or a radius around one of its offices. A home visit outside every area is refused before anyone has to call back.

    24 methods

Base\Office\Entity\Share

  • AccessLog class

    Who did what with a document, when, from where: each view, download, deposit, revocation - and each refusal. Rows are never edited; the document id stays when the document itself is gone.

    10 methods

  • Document class

    A document kept for someone: a result, a prescription, a report, a deed.

    35 methods

Base\Office\Entity\Visio

  • Room class

    A video room: the host (the member) and the guest (the client), opened a little before its subject's start and closed a while after its end. The guest waits in it until the host joins. What carries the call is a gateway of glitchr/omnimeet - by default from browser to browser (omnimeet/direct): the room then only relays their handshake; the room keeps which gateway opened it and its reference there. Who may enter, who is there, when it ends: all of that is judged here, whatever the gateway.

    31 methods

  • Signal class

    One message of the WebRTC handshake, from one side of a room to the other: an offer, an answer, an ICE candidate, a hang-up. Read by the other side's short polling, purged when the room closes.

    7 methods · 1 constant

Base\Office\Enum

Base\Office\Event

  • AppointmentEvent class

    Something happened to an appointment. Base\Office\Booking\BookingMailer mails the client (never the reason, never the type's name); an application may add a push, an SMS.

    1 method · 5 properties · 6 constants

  • ClientAccountEvent class

    A client's account opened from an invitation: a regime makes its own record of them (health: the patient profile).

    1 method · 2 properties · 1 constant

  • DocumentEvent class

    A document deposited or withdrawn. office.document.deposited is what mails the recipient "a document awaits" (never its title); a regime may add its own notice (a message, a push).

    1 method · 2 properties · 2 constants

Base\Office\Exception

  • BookingException class

    A booking refused, its reason a translation key of the office domain (booking.error.<key>): slot_taken, too_late, not_bookable, outside_area.

    3 methods

  • CorruptedException class

    An encrypted file or text that does not decrypt: altered, truncated, or another key's.

  • KeyMissingException class

    No master key, or not a usable one: the vault fails closed - nothing is stored in clear, nothing is read. Set it in the secrets vault: `bin/console secrets:set SHARE_MASTER_KEY` (base64 of 32 random bytes).

  • ShareException class

    A deposit refused: too large, a type not accepted, an empty file (share.error.<key>).

    3 methods

Base\Office\Form

  • AppointmentRequestType class

    The first appointment request, open to someone who has no account: what it is about (a type in request mode), with whom if they know, how to reach them, a few words - and omnibase's data-protection notice with its box to tick (Base\Form\Type\PrivacyType).

    2 methods

Base\Office\Form\Model

  • AppointmentRequest class

    What someone who has no account yet sends to ask for a first appointment.

    1 method · 7 properties

Base\Office\Guard

  • Wording class

    Words the communication of a regulated practice must not carry: comparison with colleagues, disparagement, the superlatives of advertising - what the notaries' règlement professionnel (art. 14.1, 14.2) and the lawyers' RIN (art. 10.2) refuse, each in its own terms. A regime cites its texts in its own guard and adds what is its alone (Wording::scan($text, $extra, $patterns)): omnibase/notary, omnibase/lawyer.

    3 methods · 1 constant

Base\Office\Model

  • Slot class

    A free time to book: its start and end in the office's timezone, where.

    3 methods · 3 properties

Base\Office\Repository

Base\Office\Repository\Booking

Base\Office\Repository\Share

Base\Office\Repository\Visio

  • RoomRepository class

    6 methods

  • SignalRepository class

    The handshake's storage: omnimeet/direct's rules (Omnimeet\Direct\Signaling) keep their messages here, as Signal rows - a room named by its reference on the gateway, a participant by "u<id>" (Base\Office\Visio\Rooms::participantId()).

    7 methods

Base\Office\Security\Voter

  • DocumentVoter class

    Who may see that a document exists (VIEW: its title, its date), read it (READ: open, download) or withdraw it (REVOKE):

    1 method

Base\Office\Service

  • ClientInvitations class

    Clients are invited, on omnibase's Invitations: a link mailed (the token is never stored, only its hash), valid two weeks; accepting it creates the account - the link proved the address - and attaches the appointments taken for that address before the account existed.

    5 methods · 1 constant

  • MemberRegistry class

    A member read again from their professional register through glitchr/omnistate: an RPPS number for a health professional, a bar number later - this class does not know which, ProfessionalRegistry- Interface does. What the register said is kept on the member (registryData, registryCheckedAt); what the site shows is still the office's to write.

    4 methods

Base\Office\Share

  • AudienceResolverInterface interface

    Who else may see or read a document, as a regime decides: health lets the care team read a patient's results (unless the patient objected) and the secretariat see that they exist; a notary's office lets the partners read the deeds. Autoconfigured (tag office.share_audience).

    1 method · 3 constants

  • Cipher class

    libsodium, and nothing else: a file is encrypted with a key of its own (crypto_secretstream_xchacha20poly1305, chunk by chunk, authenticated - a byte changed or a chunk missing and it does not decrypt); that key is kept wrapped by a key derived from the master key (crypto_secretbox).

    8 methods · 1 constant

  • DocumentVault class

    The document vault: a file in, encrypted to the private storage, a Document row and an access log line out; and back, decrypted, checked against its SHA-256, for whoever the DocumentVoter lets read it. Who may is decided before calling here: the vault does what it is told and writes down that it did.

    10 methods

  • ShareMailer class

    "A new document awaits you in your space": the recipient is told, and sent to sign in. The e-mail never holds the document, its title, its kind, nor a link that would open it without signing in - an e-mail is not a safe place for anything confidential (and for health data, not a lawful one).

    2 methods

Base\Office\Twig

  • OfficeExtension class

    The office in templates: office_main() (the main office), office_team(), office_walk_ins(), office_next_slots(member, type), office_room(appointment) (its video room, made when first asked), office_unread_documents(), office_layout(); the filters |office_document_title, |office_reason.

    3 methods

Base\Office\Visio

  • Gateways class

    The practice's video gateways, from glitchr/omnimeet: the one new rooms open on (office.visio.gateway, "direct" unless said otherwise) and the others configured, which rooms opened earlier may still be on.

    7 methods

  • RoomSubjectInterface interface

    What a video room is for: who hosts, who is the guest, when it starts and ends, and a key naming it ("appointment:12"). An Appointment is one; a regime may have others (a meeting of the partners with a client).

    5 methods

  • Rooms class

    The room of a subject: made the first time it is asked for, its times kept in step with the subject's (an appointment moved), opened office.visio.open_before minutes before and closed close_after minutes after the end. Closing purges the handshake.

    13 methods

  • Signaling class

    The handshake under its former name and signatures (a Room, a User): the rules are omnimeet/direct's (Omnimeet\Direct\Signaling), kept here in the Signal rows.

    3 methods · 1 constant

  • TurnCredentials class

    The relay's temporary credentials, under their former name: the code is omnimeet/direct's (Omnimeet\Direct\TurnCredentials), this service the relay of the practice's direct gateway (Gateways::turn()).

Base\Office

OfficeBundle class

class OfficeBundle extends AbstractBaseBundle OfficeBundle.php:21

A professional practice on its own site, whatever the profession: the office (address, access, team, hours, contact - the core), appointments by slot or by request (Booking), an encrypted document vault per recipient with its access log (Share), and a video room with a waiting room (Visio), its calls carried by a gateway of glitchr/omnimeet - from browser to browser by default. Nothing here knows health, law or payment: a regime (omnibase/health, later omnibase/notary, omnibase/lawyer) puts its trade on top - its templates, its compliance checks, who may read a document.

OfficeBundle::__construct()

public function __construct()

OfficeBundle::getPath()

public function getPath(): string

Modern layout: the class lives in src/, the bundle root is the package root.

OfficeBundle::build()

public function build(ContainerBuilder $container): void

Base\Office\Admin\Settings

OfficeLegalSection class

final class OfficeLegalSection implements SettingsSectionInterface OfficeLegalSection.php:14

The settings' "Office" section: what the legal notice must say and only the office knows - who hosts the site, who to write to about one's data.

OfficeLegalSection::__construct()

public function __construct(readonly TranslatorInterface $translator)

OfficeLegalSection::getPage()

public function getPage(): string

OfficeLegalSection::getFields()

public function getFields(): array

Base\Office\Admin\Widget

ComplianceWidgetType class

final class ComplianceWidgetType implements DashboardWidgetTypeInterface ComplianceWidgetType.php:13

The dashboard's "Compliance": the office's checks and its regime's, each with what to do when it is not met.

ComplianceWidgetType::__construct()

public function __construct(readonly Compliance $compliance)

ComplianceWidgetType::getName()

public static function getName(): string

ComplianceWidgetType::getTemplate()

public function getTemplate(): string

ComplianceWidgetType::getTemplateVars()

public function getTemplateVars(MenuItem $widget): array

ContactRequestsWidgetType class

final class ContactRequestsWidgetType implements DashboardWidgetTypeInterface ContactRequestsWidgetType.php:12

The dashboard's "Messages from the site": the contact requests not handled yet.

ContactRequestsWidgetType::__construct()

public function __construct(readonly ContactRequestRepository $requests)

ContactRequestsWidgetType::getName()

public static function getName(): string

ContactRequestsWidgetType::getTemplate()

public function getTemplate(): string

ContactRequestsWidgetType::getTemplateVars()

public function getTemplateVars(MenuItem $widget): array

TodayWidgetType class

final class TodayWidgetType implements DashboardWidgetTypeInterface TodayWidgetType.php:15

The dashboard's "Today": the day's appointments, of the member signed in or of everyone, and the requests waiting for a time.

TodayWidgetType::__construct()

public function __construct(readonly AppointmentRepository $appointments, readonly MemberRepository $members, readonly Security $security)

TodayWidgetType::getName()

public static function getName(): string

TodayWidgetType::getTemplate()

public function getTemplate(): string

TodayWidgetType::getTemplateVars()

public function getTemplateVars(MenuItem $widget): array

WaitingRoomWidgetType class

final class WaitingRoomWidgetType implements DashboardWidgetTypeInterface WaitingRoomWidgetType.php:13

The dashboard's "Waiting room": video rooms whose guest is there and whose host is not yet.

WaitingRoomWidgetType::__construct()

public function __construct(readonly RoomRepository $rooms)

WaitingRoomWidgetType::getName()

public static function getName(): string

WaitingRoomWidgetType::getTemplate()

public function getTemplate(): string

WaitingRoomWidgetType::getTemplateVars()

public function getTemplateVars(MenuItem $widget): array

Base\Office\Booking

AppointmentRequests class

class AppointmentRequests AppointmentRequests.php:21

A first appointment asked without an account: a practice's clients are invited, so someone new cannot go through the booking pages, which ask to sign in. The request becomes an appointment in request mode (no time yet: the practice fixes it) under the name, the e-mail and the phone given; the few words are kept encrypted, as every reason is.

AppointmentRequests::__construct()

public function __construct(readonly MemberRepository $members, readonly AppointmentTypeRepository $types, readonly BookingPolicy $policy, readonly Booker $booker)

AppointmentRequests::offer()

public function offer(): array

What can be asked for, and of whom: the types in request mode open to everyone, in their order, and the members who offer one.

AppointmentRequests::prepare()

public function prepare(array $offer, ?UserInterface $user = null, string $wanted = ''): AppointmentRequest

The request as the form opens on it: who is signed in, the member the link named (?avec=their-slug), the type when there is only one.

AppointmentRequests::memberFor()

public function memberFor(AppointmentRequest $request): ?Member

The member asked for when they offer that type, else the first who does.

AppointmentRequests::send()

public function send(AppointmentRequest $request, ?UserInterface $user = null): Appointment

Registers the request (Booker::request()): an appointment in request mode, its e-mails sent by the booking's own listeners.

Throws

BookingException when nobody takes that type online

BeneficiaryProviderInterface interface

interface BeneficiaryProviderInterface BeneficiaryProviderInterface.php:12

Who else a client may book for: health's dependants (a child, a parent cared for). Each choice has a label - what the appointment shows - and what the appointment keeps in its meta.

BeneficiaryProviderInterface::beneficiaries()

public function beneficiaries(object $user): array
Returns

keyed by an id of the provider's

Booker class

class Booker Booker.php:29

Booking, moving, cancelling. A slot is taken under a lock on the member's day, after asking the SlotFinder again; the unique slot key is the last word if two servers race. The client's reason is sealed by the Cipher before it is stored.

Booker::__construct()

public function __construct(readonly EntityManagerInterface $entityManager, readonly AppointmentRepository $appointments, readonly SlotFinder $slots, readonly BookingPolicy $policy, readonly LockFactory $locks, readonly Cipher $cipher, readonly EventDispatcherInterface $dispatcher, readonly int $cancelUntil = 24)

Booker::book()

public function book(AppointmentType $type, Member $member, \DateTimeInterface $start, ?User $client, AppointmentSource $source = AppointmentSource::ONLINE, ?string $reason = null, ?Office $office = null, ?string $address = null, array $details = [], ?User $by = null, array $meta = []): Appointment

A slot booked. Online, it must be one of the free slots and the type open to that client; by the staff, it must only not overlap another.

Booker::request()

public function request(AppointmentType $type, ?Member $member, ?User $client, ?string $message = null, ?string $address = null, array $details = [], array $meta = []): Appointment

A request without a time: the office fixes it later (confirm()). With no member chosen, the first who offers the type.

Booker::confirm()

public function confirm(Appointment $appointment, ?\DateTimeInterface $start = null, ?Member $member = null): Appointment

A request accepted, at the time the office fixes (or the slot it already had).

Booker::move()

public function move(Appointment $appointment, \DateTimeInterface $start, ?Member $member = null): Appointment

Moved by the staff: only another appointment can stop it.

Booker::cancel()

public function cancel(Appointment $appointment, bool $byStaff = false, ?\DateTimeInterface $now = null): void

Cancelled: by the client up to office.booking.cancel_until hours before, by the staff at any time.

Booker::series()

public function series(AppointmentType $type, Member $member, \DateTimeInterface $first, int $count, \DateInterval $every, ?User $client, ?string $address = null, array $details = [], ?User $by = null, array $meta = []): array

A run of appointments booked together by the staff - care every day at 8:00 for ten days: all or none, none overlapping another.

Booker::reason()

public function reason(Appointment $appointment): ?string

BookingMailer class

final class BookingMailer BookingMailer.php:23

The client's e-mails: booked (with the .ics and the Google Calendar link), request received, confirmed, moved, cancelled, reminded. They say when, with whom, where and how to cancel - never why: the reason and the type's name stay in the site.

BookingMailer::__construct()

public function __construct(readonly MailerInterface $mailer, readonly CalendarEntries $entries, readonly Ics $ics, readonly GoogleCalendarLink $google, readonly OfficeRepository $offices, readonly UrlGeneratorInterface $urls, readonly TranslatorInterface $translator, readonly string $sender, readonly string $timezone = 'Europe/Paris', readonly string $spaceRoute = 'office_space_appointments')

BookingMailer::onBooked()

public function onBooked(AppointmentEvent $event): void

BookingMailer::onRequested()

public function onRequested(AppointmentEvent $event): void

BookingMailer::onConfirmed()

public function onConfirmed(AppointmentEvent $event): void

BookingMailer::onMoved()

public function onMoved(AppointmentEvent $event): void

BookingMailer::onCancelled()

public function onCancelled(AppointmentEvent $event): void

BookingMailer::onReminder()

public function onReminder(AppointmentEvent $event): void

BookingPolicy class

class BookingPolicy BookingPolicy.php:16

Whether a client may book this type with this member online: open to everyone, to the clients the member has already seen, or to the staff alone (then by phone).

BookingPolicy::__construct()

public function __construct(readonly AppointmentRepository $appointments)

BookingPolicy::canBook()

public function canBook(AppointmentType $type, Member $member, ?UserInterface $client): bool

BookingPolicy::isListed()

public function isListed(AppointmentType $type, Member $member): bool

Whether it is shown at all before signing in (a KNOWN type is: the sign-in will tell).

CalendarEntries class

class CalendarEntries CalendarEntries.php:19

An appointment as omnibase's calendar bricks read it (Base\Service\ Calendar\Ics, GoogleCalendarLink): "Rendez-vous - <office>", with whom, where. Neither the reason nor the type's name: a calendar is synced to phones and to Google, it is not a place for them.

CalendarEntries::__construct()

public function __construct(readonly OfficeRepository $offices, readonly TranslatorInterface $translator, readonly UrlGeneratorInterface $urls, readonly string $timezone = 'Europe/Paris', readonly string $spaceRoute = 'office_space_appointments')

CalendarEntries::for()

public function for(Appointment $appointment, ?string $host = null): ?CalendarEntry

SlotFinder class

class SlotFinder SlotFinder.php:32

The free slots of one member for one appointment type: the weekly schedules, less the member's absences, less the days the office is closed (omnibase's special days), less the appointments already held and the buffer after each, less what is too soon (min_notice) or too far (horizon) - those two only for clients, not for the staff.

Times are walked in seconds from the schedule's start written in the office's timezone: on the night the clocks go back, a schedule from 1:00 to 4:00 lasts four hours and offers its 2:00 slot twice, as the clock on the wall does.

One member at a time: a restaurant's tables are another engine.

SlotFinder::__construct()

public function __construct(readonly ScheduleRepository $schedules, readonly AbsenceRepository $absences, readonly AppointmentRepository $appointments, readonly ?OpeningHours $openingHours = null, string $timezone = 'Europe/Paris', readonly int $minNotice = 120, readonly int $horizon = 60, readonly int $step = 0)

SlotFinder::timezone()

public function timezone(): \DateTimeZone

SlotFinder::find()

public function find(Member $member, AppointmentType $type, \DateTimeInterface $from, \DateTimeInterface $to, ?Office $office = null, bool $staff = false, ?\DateTimeInterface $now = null, ?Appointment $ignore = null): array
Returns

the free slots in [from, to), earliest first

SlotFinder::isFree()

public function isFree(Member $member, AppointmentType $type, \DateTimeInterface $start, ?Office $office = null, bool $staff = false, ?\DateTimeInterface $now = null, ?Appointment $ignore = null): bool

Whether that start is one of the free slots (the booking checks it again under its lock).

SlotFinder::next()

public function next(Member $member, AppointmentType $type, int $count = 3, ?Office $office = null, ?\DateTimeInterface $now = null): array
Returns

the next free slots, looking up to the horizon

SlotFinder::compute()

public function compute(array $schedules, array $absences, array $busy, AppointmentType $type, \DateTimeInterface $from, \DateTimeInterface $to, \DateTimeInterface $now, bool $staff = false): array

The computation itself, on what was read: testable without a database.

$busy

held times, buffers included

Base\Office\Command

CloseDayCommand class

class CloseDayCommand extends Command CloseDayCommand.php:21

The day closed, from the cron container each night: a confirmed appointment now past is DONE - a video one whose guest never came into the room is a NO_SHOW (in person, the staff marks it) - and the closed rooms' handshakes are purged.

CloseDayCommand::__construct()

public function __construct(readonly AppointmentRepository $appointments, readonly RoomRepository $rooms, readonly Rooms $roomService, readonly EntityManagerInterface $entityManager)

RemindCommand class

class RemindCommand extends Command RemindCommand.php:20

The reminders, from the cron container every quarter of an hour: each confirmed appointment gets one reminder per office.booking.reminders entry (24 and 2 hours before by default), once.

RemindCommand::__construct()

public function __construct(readonly AppointmentRepository $appointments, readonly EntityManagerInterface $entityManager, readonly EventDispatcherInterface $dispatcher, readonly array $reminders = [24, 2])

Base\Office\Compliance

Compliance class

class Compliance Compliance.php:8

Every check, run: the widget's list.

Compliance::__construct()

public function __construct(iterable $checks = [])

Compliance::run()

public function run(): array

ComplianceCheckInterface interface

interface ComplianceCheckInterface ComplianceCheckInterface.php:11

One thing the back office's "Compliance" widget checks: the vault's key is set, the legal notice names the host, the fees are displayed... The office has its own; each regime adds its trade's (autoconfigured, tag office.compliance_check).

ComplianceCheckInterface::check()

public function check(): ComplianceResult

ComplianceResult class

final readonly class ComplianceResult ComplianceResult.php:5

const OK = 'ok'
const WARNING = 'warning'
const MISSING = 'missing'
public string $label

A translation key of the domain given (its label).

public string $status
public ?string $advice

What to do about it, a translation key too; null when all is well.

public string $domain
public array $parameters

ComplianceResult::__construct()

public function __construct(public string $label, public string $status, public ?string $advice = null, public string $domain = 'office', public array $parameters = [])

ComplianceResult::ok()

public static function ok(string $label, string $domain = 'office'): self

ComplianceResult::isOk()

public function isOk(): bool

LegalNoticeCheck class

final class LegalNoticeCheck implements ComplianceCheckInterface LegalNoticeCheck.php:8

The legal notice names the host (and, in the settings, who to write to about one's data).

LegalNoticeCheck::__construct()

public function __construct(readonly SettingBagInterface $settings)

LegalNoticeCheck::check()

public function check(): ComplianceResult

TurnCheck class

final class TurnCheck implements ComplianceCheckInterface TurnCheck.php:14

The video's relay: configured, and answering on its first address (a UDP STUN binding request - the same port coturn listens on for TURN). It is the direct gateway's (glitchr/omnimeet, omnimeet/direct): a practice whose calls go through another gateway has no relay of its own to check.

TurnCheck::__construct()

public function __construct(readonly Gateways $gateways, readonly bool $enabled = true, readonly ?string $probe = null)

TurnCheck::check()

public function check(): ComplianceResult

TurnCheck::reachable()

public static function reachable(string $url, float $timeout = 1.0): bool

A STUN binding request (RFC 5389) over UDP; any answer will do.

VaultKeyCheck class

final class VaultKeyCheck implements ComplianceCheckInterface VaultKeyCheck.php:7

VaultKeyCheck::__construct()

public function __construct(readonly Cipher $cipher)

VaultKeyCheck::check()

public function check(): ComplianceResult

Base\Office\Controller\Admin

AdminPageTrait trait

trait AdminPageTrait AdminPageTrait.php:12

A page of the bundle's in the back office's chrome (it extends @Admin/layout.html.twig): its menus built when no CRUD did.

AdminPageTrait::setAdminChrome()

public function setAdminChrome(AdminContext $adminContext, MenuBuilder $menuBuilder): void

AgendaController class

class AgendaController extends AbstractController AgendaController.php:36

The agenda, in the back office: a day (every member a column) or a week (one member, a day a column), drawn by hand on a quarter-hour grid; the quick booking the secretariat takes on the phone - an existing client or a new one, who is then invited to open their account; done, no-show, cancel, move. A member sees their own columns' reasons; the others see that the time is taken.

const START_HOUR = 7
const END_HOUR = 20

AgendaController::__construct()

public function __construct(readonly MemberRepository $members, readonly AppointmentRepository $appointments, readonly AppointmentTypeRepository $types, readonly ScheduleRepository $schedules, readonly AbsenceRepository $absences, readonly RoomRepository $rooms, readonly SlotFinder $slots, readonly Booker $booker, readonly TranslatorInterface $translator, readonly EntityManagerInterface $entityManager)

AgendaController::index()

public function index(Request $request): Response

AgendaController::book()

public function book(Request $request, ClientInvitations $invitations): Response

The quick booking: taken on the phone or at the desk.

AgendaController::action()

public function action(Request $request, Appointment $appointment, string $action): Response

done | no_show | cancel | confirm | move (with date and time).

DocumentsController class

class DocumentsController extends AbstractController DocumentsController.php:28

The vault from the office's side: sending a document to a client (who is mailed "a document awaits", nothing more), the list of what was sent - a title is shown only to whom the DocumentVoter lets see it -, and the access log for the coordination.

DocumentsController::__construct()

public function __construct(readonly DocumentVault $vault, readonly DocumentRepository $documents, readonly EntityManagerInterface $entityManager, readonly TranslatorInterface $translator)

DocumentsController::index()

public function index(Request $request): Response

DocumentsController::send()

public function send(Request $request): Response

DocumentsController::revoke()

public function revoke(Request $request, int $id): Response

DocumentsController::log()

public function log(Request $request, AccessLogRepository $logs): Response

Who looked at what: for the coordination only.

OpenToTrait trait

trait OpenToTrait OpenToTrait.php:13

The office's screens are written by the office - its coordination (ROLE_ADMIN) or its staff (ROLE_STAFF: the agenda's screens) - not only by the super-admin omnibase/admin asks by default for anything that writes.

Base\Office\Controller\Admin\Crud

AbsenceCrudController class

class AbsenceCrudController extends AbstractCrudController AbsenceCrudController.php:24

A member away: their slots are gone for that time.

AbsenceCrudController::setTranslator()

public function setTranslator(TranslatorInterface $translator): void

AbsenceCrudController::getEntityFqcn()

public static function getEntityFqcn(): string

AbsenceCrudController::getPreferredIcon()

public static function getPreferredIcon(): ?string

AbsenceCrudController::configureCrud()

public function configureCrud(Crud $crud): Crud

AbsenceCrudController::configureActions()

public function configureActions(Actions $actions): Actions

AbsenceCrudController::configureFields()

public function configureFields(string $pageName): iterable

AppointmentCrudController class

class AppointmentCrudController extends AbstractCrudController AppointmentCrudController.php:27

Every appointment, as a list (the agenda is the screen to work in): who, when, its status. The client's reason is not shown here.

AppointmentCrudController::setTranslator()

public function setTranslator(TranslatorInterface $translator): void

AppointmentCrudController::getEntityFqcn()

public static function getEntityFqcn(): string

AppointmentCrudController::getPreferredIcon()

public static function getPreferredIcon(): ?string

AppointmentCrudController::configureCrud()

public function configureCrud(Crud $crud): Crud

AppointmentCrudController::configureActions()

public function configureActions(Actions $actions): Actions

AppointmentCrudController::configureFields()

public function configureFields(string $pageName): iterable

AppointmentTypeCrudController class

class AppointmentTypeCrudController extends AbstractCrudController AppointmentTypeCrudController.php:32

What an appointment is for: duration, buffer, channel, who may book it online, by slot or by request.

AppointmentTypeCrudController::setTranslator()

public function setTranslator(TranslatorInterface $translator): void

AppointmentTypeCrudController::getEntityFqcn()

public static function getEntityFqcn(): string

AppointmentTypeCrudController::getPreferredIcon()

public static function getPreferredIcon(): ?string

AppointmentTypeCrudController::configureCrud()

public function configureCrud(Crud $crud): Crud

AppointmentTypeCrudController::configureActions()

public function configureActions(Actions $actions): Actions

AppointmentTypeCrudController::configureFields()

public function configureFields(string $pageName): iterable

ContactRequestCrudController class

class ContactRequestCrudController extends AbstractCrudController ContactRequestCrudController.php:26

The contact form's messages, read and marked handled; purged after office.contact.retention_months.

ContactRequestCrudController::setTranslator()

public function setTranslator(TranslatorInterface $translator): void

ContactRequestCrudController::getEntityFqcn()

public static function getEntityFqcn(): string

ContactRequestCrudController::getPreferredIcon()

public static function getPreferredIcon(): ?string

ContactRequestCrudController::configureCrud()

public function configureCrud(Crud $crud): Crud

ContactRequestCrudController::configureActions()

public function configureActions(Actions $actions): Actions

ContactRequestCrudController::configureFields()

public function configureFields(string $pageName): iterable

MemberCrudController class

class MemberCrudController extends AbstractCrudController MemberCrudController.php:33

The team as the site shows it. "Read the register" asks the member's professional register again (glitchr/omnistate) and keeps what it said.

MemberCrudController::setTranslator()

public function setTranslator(TranslatorInterface $translator): void

MemberCrudController::getEntityFqcn()

public static function getEntityFqcn(): string

MemberCrudController::getPreferredIcon()

public static function getPreferredIcon(): ?string

MemberCrudController::configureCrud()

public function configureCrud(Crud $crud): Crud

MemberCrudController::configureActions()

public function configureActions(Actions $actions): Actions

MemberCrudController::configureFields()

public function configureFields(string $pageName): iterable

MemberCrudController::setRegistry()

public function setRegistry(MemberRegistry $registry): void

MemberCrudController::registry()

public function registry(Request $request, string $entityId): Response

The member read again from their register.

OfficeCrudController class

class OfficeCrudController extends AbstractCrudController OfficeCrudController.php:28

The office's places: address, phones, access, accessibility. The opening hours are omnibase's (the back office's hours screen).

OfficeCrudController::setTranslator()

public function setTranslator(TranslatorInterface $translator): void

OfficeCrudController::getEntityFqcn()

public static function getEntityFqcn(): string

OfficeCrudController::getPreferredIcon()

public static function getPreferredIcon(): ?string

OfficeCrudController::configureCrud()

public function configureCrud(Crud $crud): Crud

OfficeCrudController::configureActions()

public function configureActions(Actions $actions): Actions

OfficeCrudController::configureFields()

public function configureFields(string $pageName): iterable

ScheduleCrudController class

class ScheduleCrudController extends AbstractCrudController ScheduleCrudController.php:25

The weekly schedules: who receives when, where, for which types.

ScheduleCrudController::setTranslator()

public function setTranslator(TranslatorInterface $translator): void

ScheduleCrudController::getEntityFqcn()

public static function getEntityFqcn(): string

ScheduleCrudController::getPreferredIcon()

public static function getPreferredIcon(): ?string

ScheduleCrudController::configureCrud()

public function configureCrud(Crud $crud): Crud

ScheduleCrudController::configureActions()

public function configureActions(Actions $actions): Actions

ScheduleCrudController::configureFields()

public function configureFields(string $pageName): iterable

ServiceAreaCrudController class

class ServiceAreaCrudController extends AbstractCrudController ServiceAreaCrudController.php:26

Where the office goes to its clients: postcodes, towns, or a radius.

ServiceAreaCrudController::setTranslator()

public function setTranslator(TranslatorInterface $translator): void

ServiceAreaCrudController::getEntityFqcn()

public static function getEntityFqcn(): string

ServiceAreaCrudController::getPreferredIcon()

public static function getPreferredIcon(): ?string

ServiceAreaCrudController::configureActions()

public function configureActions(Actions $actions): Actions

ServiceAreaCrudController::configureFields()

public function configureFields(string $pageName): iterable

WalkInCrudController class

class WalkInCrudController extends AbstractCrudController WalkInCrudController.php:26

Hours without an appointment: the blood tests at 7:30, a walk-in morning.

WalkInCrudController::setTranslator()

public function setTranslator(TranslatorInterface $translator): void

WalkInCrudController::getEntityFqcn()

public static function getEntityFqcn(): string

WalkInCrudController::getPreferredIcon()

public static function getPreferredIcon(): ?string

WalkInCrudController::configureActions()

public function configureActions(Actions $actions): Actions

WalkInCrudController::configureFields()

public function configureFields(string $pageName): iterable

Base\Office\Controller\Client

AbstractRequestController class

abstract class AbstractRequestController extends AbstractController AbstractRequestController.php:33

The page where a first appointment is asked without an account (Base\Office\Booking\AppointmentRequests), for the bundle's own route and for a regime's: the regime routes an action of its own and answers with respond(), naming its template, the translation domain of its error and the prefix of its labels.

#[Route('/rendez-vous/demande', name: 'notary_request', methods: ['GET', 'POST'], priority: 10)] public function request(Request $request): Response { return $this->respond($request, '@Notary/client/request.html.twig', 'notary', '@notary.request'); }

The template receives form, sent, available (something can be asked for) and office (the main one).

AbstractRequestController::setRequestServices()

public function setRequestServices(AppointmentRequests $requests, OfficeRepository $offices, TranslatorInterface $translator): void

BookingController class

class BookingController extends AbstractController BookingController.php:38

Booking online, one step a page so that it works on a phone and without scripts: who → what for → when → (signed in) confirm → done. A type in request mode asks instead of picking a slot. Cancelling needs the link of the e-mail, or the client's space.

BookingController::__construct()

public function __construct(readonly MemberRepository $members, readonly AppointmentTypeRepository $types, readonly AppointmentRepository $appointments, readonly SlotFinder $slots, readonly BookingPolicy $policy, readonly Booker $booker, readonly TranslatorInterface $translator, iterable $beneficiaries = [], readonly int $horizon = 60)

BookingController::index()

public function index(): Response

BookingController::member()

public function member(Member $member): Response

BookingController::slots()

public function slots(Request $request, Member $member, AppointmentType $type): Response

BookingController::confirm()

public function confirm(Request $request, Member $member, AppointmentType $type, string $start): Response

BookingController::done()

public function done(Appointment $appointment, CalendarEntries $entries, GoogleCalendarLink $google): Response

BookingController::ics()

public function ics(Appointment $appointment, CalendarEntries $entries, Ics $ics, Request $request): Response

BookingController::cancel()

public function cancel(Request $request, string $token): Response

The link of the e-mail: no sign-in needed, the token is the proof.

BookingController::space()

public function space(): Response

BookingController::spaceCancel()

public function spaceCancel(Request $request, Appointment $appointment): Response

InvitationController class

class InvitationController extends AbstractController InvitationController.php:15

The invitation's link: choose an identifier and a password, the account is opened.

InvitationController::accept()

public function accept(Request $request, string $token, ClientInvitations $invitations, EntityManagerInterface $entityManager, Security $security, TranslatorInterface $translator): Response

OfficeController class

class OfficeController extends AbstractController OfficeController.php:27

The office's public pages: the team, a member's page, the hours and how to come, the contact form. Each template can be the regime's own (office.templates.*): health shows practitioners with their fees.

OfficeController::__construct()

public function __construct(readonly OfficeRepository $offices, readonly MemberRepository $members, readonly string $teamTemplate, readonly string $memberTemplate, readonly string $hoursTemplate, readonly string $contactTemplate)

OfficeController::team()

public function team(): Response

OfficeController::member()

public function member(string $slug): Response

OfficeController::hours()

public function hours(WalkInRepository $walkIns): Response

OfficeController::contact()

public function contact(Request $request, FormFactoryInterface $forms, EntityManagerInterface $entityManager, MailerInterface $mailer, string $recipient, string $sender, string $notice): Response

RequestController class

class RequestController extends AbstractRequestController RequestController.php:19

The bundle's own page for a first appointment asked without an account, for a practice whose regime has none of its own. Off by default (office.booking.public_request: true opens it): the booking pages ask to sign in, and whether someone unknown may ask is the practice's choice.

A regime's own route at the same address (notary_request, lawyer_request, priority 10) comes first.

RequestController::__construct()

public function __construct(readonly bool $enabled = false, readonly string $template = '@Office/client/request.html.twig')

RequestController::request()

public function request(Request $request): Response

ShareController class

class ShareController extends AbstractController ShareController.php:32

The client's documents: the list, a download, a deposit. A file only ever leaves through here: signed in, allowed by the DocumentVoter, written in the access log, sent with no-store, nosniff and as an attachment (an image may be shown inline, sandboxed, in a conversation).

Someone else's document answers 404, and the refusal is logged.

ShareController::__construct()

public function __construct(readonly DocumentVault $vault, readonly DocumentRepository $documents, readonly TranslatorInterface $translator, readonly EntityManagerInterface $entityManager)

ShareController::index()

public function index(): Response

ShareController::download()

public function download(Request $request, int $id): Response

ShareController::deposit()

public function deposit(Request $request): Response

A deposit by the client: a prescription to bring, an insurance card - theirs, shown to whom the regime says.

ShareController::revoke()

public function revoke(Request $request, Document $document): Response

VisioController class

class VisioController extends AbstractController VisioController.php:39

The video room: its page (camera check, waiting room, the call), and the small JSON endpoints its script asks - the ICE servers with fresh TURN credentials, the handshake (post, poll), "I am here", a file dropped during the call (kept in the vault for the guest). Only the room's two participants get anything, and only while it is open.

What carries the call is the room's gateway (glitchr/omnimeet): the page asks it how this participant enters and renders that - omnimeet/direct's engine on these endpoints, a provider's frame, or a button to its address. Who gets in, who is there and when it ends are judged here, on the Room, whatever the gateway.

VisioController::__construct()

public function __construct(readonly RoomRepository $rooms, readonly Rooms $service, readonly Signaling $signaling, readonly TranslatorInterface $translator, readonly ?LoggerInterface $logger = null)

VisioController::room()

public function room(Request $request, string $token): Response

VisioController::engine()

public function engine(string $token): Response

The script of the room's gateway (omnimeet/direct's engine, the script that drives a provider's frame), served from its package.

VisioController::ice()

public function ice(string $token): JsonResponse

VisioController::send()

public function send(Request $request, string $token): JsonResponse

VisioController::poll()

public function poll(Request $request, string $token): JsonResponse

The other side's messages after after, and who is there.

VisioController::end()

public function end(Request $request, string $token): Response

VisioController::document()

public function document(Request $request, string $token, DocumentVault $vault): JsonResponse

A file dropped during the call: kept in the vault for the guest, from whoever sent it.

Base\Office\DependencyInjection

OfficeConfiguration class

class OfficeConfiguration extends AbstractBaseConfiguration OfficeConfiguration.php:8

OfficeConfiguration::getConfigTreeBuilder()

public function getConfigTreeBuilder(): TreeBuilder

OfficeExtension class

class OfficeExtension extends AbstractBaseExtension implements PrependExtensionInterface OfficeExtension.php:12

const VISIO_ALIASES = ['turn_secret', 'turn_urls', 'stun_urls', 'turn_ttl']

The former places of the direct gateway's options, kept as aliases for one version: office.visio.<key>.

OfficeExtension::getConfiguration()

public function getConfiguration(array $config, ContainerBuilder $container): OfficeConfiguration

OfficeExtension::visioAliases()

public static function visioAliases(array $configs): array

The direct gateway's options as office.visio still carries them (turn_secret, turn_urls, stun_urls, turn_ttl): read as they are written, the last file that sets one winning. Their place is now omnimeet.gateways.direct.options.

$configs

office's configurations, unprocessed

Returns

the options, and the aliases a file still sets

OfficeExtension::prepend()

public function prepend(ContainerBuilder $container): void

The practice's name for the texts, the direct video gateway, and the private storage of the vault when the application has not declared it.

(The UTC moment type the entities use, utc_datetime_immutable, is glitchr/omnibase's: Base\Database\Type\UtcDateTimeImmutableType.)

OfficeExtension::load()

public function load(array $configs, ContainerBuilder $container): void

Vocabulary class

final class Vocabulary Vocabulary.php:18

How the practice is named in the bundle's texts: "le cabinet" for a law firm, "l'étude" or "l'office" for a notary's, "la maison de santé"... office.vocabulary.practice names a preset or gives the forms; they reach every translation as global parameters (framework.translator.globals):

{practice} cabinet étude maison de santé {the_practice} le cabinet l’étude la maison de santé {The_practice} Le cabinet L’étude La maison de santé {at_the_practice} au cabinet à l’étude à la maison de santé {At_the_practice} Au cabinet À l’étude À la maison de santé {of_the_practice} du cabinet de l’étude de la maison de santé

const PRESETS = ['cabinet' => ['name' => 'cabinet', 'the' => 'le cabinet', 'at' => 'au cabinet', 'of' => 'du cabinet'], 'etude' => ['name' => 'étude', 'the' => 'l’étude', 'a…

Vocabulary::globals()

public static function globals(string|array $practice = 'cabinet'): array
$practice

a preset's name, or the forms (what is missing is built from the name)

Returns

the global translation parameters

Base\Office\DependencyInjection\Compiler

VisioGatewayPass class

final class VisioGatewayPass implements CompilerPassInterface VisioGatewayPass.php:19

An application that has not registered glitchr/omnimeet's bundle (Omnimeet\Bridge\Symfony\OmnimeetBundle) still has its video room: the registry is made here with the direct gateway alone, its relay read from office.visio's own keys - what the bundle did before the family existed.

Any other gateway is declared under omnimeet.gateways, with the bundle.

VisioGatewayPass::process()

public function process(ContainerBuilder $container): void

Base\Office\Entity

Area class

abstract class Area Area.php:37

A field of practice (family, real estate, labour...): what it covers, what is done in it, who of the team follows it. Information for the public, whose wording a regime checks (Base\Office\Guard\Wording).

A mapped superclass: each regime has its own entity and its own table, and names what is done in the field its own way - a notary's deeds, a lawyer's matters:

#[ORM\Entity(repositoryClass: AreaRepository::class)]
#[ORM\Table(name: 'notary_area')]
class Area extends \Base\Office\Entity\Area
{
    #[ORM\Column(type: 'json')]
    protected array $deeds = [];
#[ORM\ManyToMany(targetEntity: Member::class)]
#[ORM\JoinTable(name: 'notary_area_member')]
protected Collection $members;
    public function getItems(): array { return $this->deeds; }
    public function setItems(?array $items): static { $this->deeds = self::lines($items); return $this; }
}

The list and the members are mapped by the entity (a column and a join table of its own names); everything else is here. The repository: Base\Office\Repository\AreaRepository.

Area::__construct()

public function __construct(string $name = '', ?string $slug = null)

Area::__toString()

public function __toString(): string

Area::getItems()

abstract public function getItems(): array
Returns

what is done in that field: a notary's deeds, a lawyer's matters

Area::setItems()

abstract public function setItems(?array $items): static

Area::getItemsText()

public function getItemsText(): string

The list, one a line: what the back office's form edits.

Area::setItemsText()

public function setItemsText(?string $text): static

Area::getId()

public function getId(): ?int

Area::getName()

public function getName(): string

Area::setName()

public function setName(?string $name): static

Area::getSlug()

public function getSlug(): string

Area::setSlug()

public function setSlug(?string $slug): static

Area::getSummary()

public function getSummary(): ?string

Area::setSummary()

public function setSummary(?string $summary): static

Area::getBody()

public function getBody(): ?string

Area::setBody()

public function setBody(?string $body): static

Area::getMembers()

public function getMembers(): Collection

Area::addMember()

public function addMember(Member $member): static

Area::removeMember()

public function removeMember(Member $member): static

Area::isActive()

public function isActive(): bool

Area::setActive()

public function setActive(bool $active): static

Area::getPosition()

public function getPosition(): int

Area::setPosition()

public function setPosition(?int $position): static

Area::getParagraphs()

public function getParagraphs(): array
Returns

the body's paragraphs

Area::getPublicText()

public function getPublicText(): string

Everything written for the public, for the wording check.

ContactRequest class

class ContactRequest ContactRequest.php:12

A message from the contact form, kept for the back office as long as the form's notice says (office.contact.retention_months), then purged.

ContactRequest::__construct()

public function __construct(string $name = '', string $email = '', string $message = '', ?string $phone = null)

ContactRequest::__toString()

public function __toString(): string

ContactRequest::getId()

public function getId(): ?int

ContactRequest::getName()

public function getName(): string

ContactRequest::getEmail()

public function getEmail(): string

ContactRequest::getPhone()

public function getPhone(): ?string

ContactRequest::getMessage()

public function getMessage(): string

ContactRequest::getCreatedAt()

public function getCreatedAt(): \DateTimeImmutable

ContactRequest::getHandledAt()

public function getHandledAt(): ?\DateTimeImmutable

ContactRequest::isHandled()

public function isHandled(): bool

ContactRequest::setHandled()

public function setHandled(bool $handled): self

Invitation class

class Invitation extends Invitation Invitation.php:12

A client invited to open their account (omnibase's invitation by token): the secretary took an appointment on the phone for someone new - nobody signs up by themselves into an office's client space.

Invitation::getId()

public function getId(): ?int

Invitation::getPhone()

public function getPhone(): ?string

Invitation::setPhone()

public function setPhone(?string $phone): static

Member class

class Member Member.php:19

Someone of the team, as the site presents them: a portrait, a title, the languages they speak, a biography - and, for a regulated profession, their number in its register (registryId: an RPPS number, a bar number), read again by Base\Office\Service\MemberRegistry. The account they sign in with is optional: a retired partner or a replacement may be on the page without one. Appointments are booked with a Member.

Member::__construct()

public function __construct(string $displayName = '', ?string $slug = null)

Member::__toString()

public function __toString(): string

Member::getId()

public function getId(): ?int

Member::getUser()

public function getUser(): ?User

Member::setUser()

public function setUser(?User $user): self

Member::getSlug()

public function getSlug(): string

Member::setSlug()

public function setSlug(?string $slug): self

Member::getDisplayName()

public function getDisplayName(): string

Member::setDisplayName()

public function setDisplayName(?string $name): self

Member::getTitle()

public function getTitle(): ?string

Member::setTitle()

public function setTitle(?string $title): self

Member::getCategory()

public function getCategory(): ?string

Member::setCategory()

public function setCategory(?string $category): self

Member::getPortrait()

public function getPortrait(): ?string

Member::setPortrait()

public function setPortrait(?string $portrait): self

Member::getLanguages()

public function getLanguages(): array

Member::setLanguages()

public function setLanguages(?array $languages): self

Member::getBiography()

public function getBiography(): ?string

Member::setBiography()

public function setBiography(?string $biography): self

Member::getRegistryId()

public function getRegistryId(): ?string

Member::setRegistryId()

public function setRegistryId(?string $registryId): self

Member::getRegistryCheckedAt()

public function getRegistryCheckedAt(): ?\DateTimeImmutable

Member::getRegistryData()

public function getRegistryData(): ?array

Member::setRegistryData()

public function setRegistryData(?array $data, ?\DateTimeImmutable $checkedAt = null): self

Member::getOffices()

public function getOffices(): Collection

Member::addOffice()

public function addOffice(Office $office): self

Member::removeOffice()

public function removeOffice(Office $office): self

Member::getColor()

public function getColor(): ?string

Member::setColor()

public function setColor(?string $color): self

Member::isVisible()

public function isVisible(): bool

Member::setVisible()

public function setVisible(bool $visible): self

Member::isActive()

public function isActive(): bool

Member::setActive()

public function setActive(bool $active): self

Member::isBookable()

public function isBookable(): bool

Member::setBookable()

public function setBookable(bool $bookable): self

Member::getPosition()

public function getPosition(): int

Member::setPosition()

public function setPosition(?int $position): self

Member::getInitials()

public function getInitials(): string

The initials shown when there is no portrait.

Office class

class Office Office.php:14

A place where the practice receives: its address, how to reach it, what it offers to someone in a wheelchair, its phones and languages. Most practices have one; a second site, a branch, is a second Office. The opening hours are omnibase's (Base\Service\OpeningHours), not a copy.

Office::__construct()

public function __construct(string $name = '', string $slug = '')

Office::__toString()

public function __toString(): string

Office::slugify()

public static function slugify(string $text): string

Office::getId()

public function getId(): ?int

Office::getName()

public function getName(): string

Office::setName()

public function setName(?string $name): self

Office::getSlug()

public function getSlug(): string

Office::setSlug()

public function setSlug(?string $slug): self

Office::getStreet()

public function getStreet(): ?string

Office::setStreet()

public function setStreet(?string $street): self

Office::getPostalCode()

public function getPostalCode(): ?string

Office::setPostalCode()

public function setPostalCode(?string $postalCode): self

Office::getCity()

public function getCity(): ?string

Office::setCity()

public function setCity(?string $city): self

Office::getCountry()

public function getCountry(): string

Office::setCountry()

public function setCountry(?string $country): self

Office::getLatitude()

public function getLatitude(): ?float

Office::setLatitude()

public function setLatitude(?float $latitude): self

Office::getLongitude()

public function getLongitude(): ?float

Office::setLongitude()

public function setLongitude(?float $longitude): self

Office::getPhone()

public function getPhone(): ?string

Office::setPhone()

public function setPhone(?string $phone): self

Office::getSecondaryPhone()

public function getSecondaryPhone(): ?string

Office::setSecondaryPhone()

public function setSecondaryPhone(?string $phone): self

Office::getSecondaryPhoneLabel()

public function getSecondaryPhoneLabel(): ?string

Office::setSecondaryPhoneLabel()

public function setSecondaryPhoneLabel(?string $label): self

Office::getEmail()

public function getEmail(): ?string

Office::setEmail()

public function setEmail(?string $email): self

Office::getAccess()

public function getAccess(): ?string

Office::setAccess()

public function setAccess(?string $access): self

Office::isWheelchairAccessible()

public function isWheelchairAccessible(): bool

Office::setWheelchairAccessible()

public function setWheelchairAccessible(bool $accessible): self

Office::getAccessibility()

public function getAccessibility(): ?string

Office::setAccessibility()

public function setAccessibility(?string $accessibility): self

Office::getParking()

public function getParking(): ?string

Office::setParking()

public function setParking(?string $parking): self

Office::getLanguages()

public function getLanguages(): array

Office::setLanguages()

public function setLanguages(?array $languages): self

Office::isMain()

public function isMain(): bool

Office::setMain()

public function setMain(bool $main): self

Office::getPosition()

public function getPosition(): int

Office::setPosition()

public function setPosition(?int $position): self

Office::getAddress()

public function getAddress(): string

"12 rue des Tilleuls, 67000 Strasbourg"

Office::toSchema()

public function toSchema(): array

schema.org PostalAddress.

Office::getMapUrl()

public function getMapUrl(): ?string

An OpenStreetMap link to the place: no tracker, no key.

WalkIn class

class WalkIn WalkIn.php:13

Hours when the practice receives without an appointment: the nurses' blood tests from 7:30 to 8:30, a walk-in consultation on Saturday morning. Shown with the opening hours; not bookable.

WalkIn::__construct()

public function __construct(string $label = '', array $days = [], string $opensAt = '07:30', string $closesAt = '08:30')

WalkIn::__toString()

public function __toString(): string

WalkIn::getId()

public function getId(): ?int

WalkIn::getLabel()

public function getLabel(): string

WalkIn::setLabel()

public function setLabel(?string $label): self

WalkIn::getDays()

public function getDays(): array

WalkIn::setDays()

public function setDays(?array $days): self

WalkIn::getOpensAt()

public function getOpensAt(): string

WalkIn::setOpensAt()

public function setOpensAt(?string $time): self

WalkIn::getClosesAt()

public function getClosesAt(): string

WalkIn::setClosesAt()

public function setClosesAt(?string $time): self

WalkIn::getOffice()

public function getOffice(): ?Office

WalkIn::setOffice()

public function setOffice(?Office $office): self

WalkIn::getMember()

public function getMember(): ?Member

WalkIn::setMember()

public function setMember(?Member $member): self

WalkIn::getNotes()

public function getNotes(): ?string

WalkIn::setNotes()

public function setNotes(?string $notes): self

WalkIn::isActive()

public function isActive(): bool

WalkIn::setActive()

public function setActive(bool $active): self

WalkIn::isOn()

public function isOn(int $isoDay): bool

Base\Office\Entity\Booking

Absence class

class Absence Absence.php:14

A member away: holidays, a training, an afternoon off. Their slots are gone for that time. The whole office closed is a special day of omnibase's opening hours (Base\Entity\Hours\SpecialDay), not an absence.

Absence::__construct()

public function __construct(?Member $member = null, ?\DateTimeInterface $startsAt = null, ?\DateTimeInterface $endsAt = null, ?string $reason = null)

Absence::__toString()

public function __toString(): string

Absence::getId()

public function getId(): ?int

Absence::getMember()

public function getMember(): ?Member

Absence::setMember()

public function setMember(?Member $member): self

Absence::getStartsAt()

public function getStartsAt(): \DateTimeImmutable

Absence::setStartsAt()

public function setStartsAt(?\DateTimeInterface $at): self

Absence::getEndsAt()

public function getEndsAt(): \DateTimeImmutable

Absence::setEndsAt()

public function setEndsAt(?\DateTimeInterface $at): self

Absence::getReason()

public function getReason(): ?string

Absence::setReason()

public function setReason(?string $reason): self

Absence::overlaps()

public function overlaps(\DateTimeInterface $start, \DateTimeInterface $end): bool

Appointment class

class Appointment implements RoomSubjectInterface Appointment.php:27

An appointment with a member: when (or not yet, for a request), what for, where - an office, an address for a home visit, a video room - and who for: the client's account, or a name taken on the phone, perhaps a relative (a child, a parent cared for: beneficiary). The reason the client typed is kept encrypted (reasonCipher: Base\Office\Share\Cipher); no e-mail ever carries it.

slotKey holds "member|start" while the appointment holds its slot and is emptied when it no longer does: its unique index is what refuses a second booking of the same slot, whatever two requests race.

Appointment::__construct()

public function __construct(?Member $member = null, ?AppointmentType $type = null, ?\DateTimeInterface $startsAt = null)

Appointment::__toString()

public function __toString(): string

Appointment::getId()

public function getId(): ?int

Appointment::getMember()

public function getMember(): ?Member

Appointment::setMember()

public function setMember(?Member $member): self

Appointment::getType()

public function getType(): ?AppointmentType

Appointment::setType()

public function setType(?AppointmentType $type): self

Appointment::getOffice()

public function getOffice(): ?Office

Appointment::setOffice()

public function setOffice(?Office $office): self

Appointment::getAddress()

public function getAddress(): ?string

Appointment::setAddress()

public function setAddress(?string $address): self

Appointment::getStartsAt()

public function getStartsAt(): ?\DateTimeImmutable

Appointment::getEndsAt()

public function getEndsAt(): ?\DateTimeImmutable

Appointment::setStartsAt()

public function setStartsAt(?\DateTimeInterface $at): self

The start, and the end from the type's duration.

Appointment::setEndsAt()

public function setEndsAt(?\DateTimeInterface $at): self

Appointment::getClient()

public function getClient(): ?User

Appointment::setClient()

public function setClient(?User $client): self

Appointment::getClientName()

public function getClientName(): ?string

Appointment::setClientName()

public function setClientName(?string $name): self

Appointment::getClientEmail()

public function getClientEmail(): ?string

Appointment::setClientEmail()

public function setClientEmail(?string $email): self

Appointment::getClientPhone()

public function getClientPhone(): ?string

Appointment::setClientPhone()

public function setClientPhone(?string $phone): self

Appointment::getBeneficiary()

public function getBeneficiary(): ?string

Appointment::setBeneficiary()

public function setBeneficiary(?string $beneficiary): self

Appointment::getStatus()

public function getStatus(): AppointmentStatus

Appointment::setStatus()

public function setStatus(AppointmentStatus|string $status): self

Appointment::getStatusValue()

public function getStatusValue(): string

Appointment::setStatusValue()

public function setStatusValue(?string $value): self

Appointment::isActive()

public function isActive(): bool

Appointment::isCancelled()

public function isCancelled(): bool

Appointment::getSource()

public function getSource(): AppointmentSource

Appointment::setSource()

public function setSource(AppointmentSource|string $source): self

Appointment::getReasonCipher()

public function getReasonCipher(): ?string

Appointment::setReasonCipher()

public function setReasonCipher(?string $cipher): self

Appointment::getSlotKey()

public function getSlotKey(): ?string

Appointment::getCancelTokenHash()

public function getCancelTokenHash(): ?string

Appointment::setCancelTokenHash()

public function setCancelTokenHash(?string $hash): self

Appointment::getRemindersSent()

public function getRemindersSent(): array

Appointment::markReminderSent()

public function markReminderSent(int $hours): self

Appointment::hasReminderBeenSent()

public function hasReminderBeenSent(int $hours): bool

Appointment::resetReminders()

public function resetReminders(): self

Appointment::getSeries()

public function getSeries(): ?string

Appointment::setSeries()

public function setSeries(?string $series): self

Appointment::getMeta()

public function getMeta(): array

Appointment::getMetaValue()

public function getMetaValue(string $key, mixed $default = null): mixed

Appointment::setMetaValue()

public function setMetaValue(string $key, mixed $value): self

Appointment::getCreatedBy()

public function getCreatedBy(): ?User

Appointment::setCreatedBy()

public function setCreatedBy(?User $user): self

Appointment::getCreatedAt()

public function getCreatedAt(): \DateTimeImmutable

Appointment::getCancelledAt()

public function getCancelledAt(): ?\DateTimeImmutable

Appointment::getChannel()

public function getChannel(): Channel

Appointment::isVideo()

public function isVideo(): bool

Appointment::isUpcoming()

public function isUpcoming(?\DateTimeInterface $now = null): bool

Appointment::slotKeyFor()

public static function slotKeyFor(Member $member, \DateTimeInterface $start): string

The slot key: who and when, the same text for the same slot whatever the timezone it was written in.

Appointment::getVisioHost()

public function getVisioHost(): ?User

Appointment::getVisioGuest()

public function getVisioGuest(): ?User

Appointment::getVisioStartsAt()

public function getVisioStartsAt(): ?\DateTimeImmutable

Appointment::getVisioEndsAt()

public function getVisioEndsAt(): ?\DateTimeImmutable

Appointment::getVisioKey()

public function getVisioKey(): string

AppointmentType class

class AppointmentType AppointmentType.php:22

What an appointment is for, as the client chooses it: "Consultation", "Téléconsultation", "Prise de sang à domicile", "Signature d'acte". Its duration and the buffer after it, how it takes place, who may book it online, whether the office confirms it by hand, what to bring - and its mode: a free slot picked by the client, or a request the office answers.

AppointmentType::__construct()

public function __construct(string $name = '', int $duration = 20, Channel $channel = Channel::IN_PERSON)

AppointmentType::__toString()

public function __toString(): string

AppointmentType::getId()

public function getId(): ?int

AppointmentType::getName()

public function getName(): string

AppointmentType::setName()

public function setName(?string $name): self

AppointmentType::getSlug()

public function getSlug(): string

AppointmentType::setSlug()

public function setSlug(?string $slug): self

AppointmentType::getDuration()

public function getDuration(): int

AppointmentType::setDuration()

public function setDuration(?int $duration): self

AppointmentType::getBuffer()

public function getBuffer(): int

AppointmentType::setBuffer()

public function setBuffer(?int $buffer): self

AppointmentType::getChannel()

public function getChannel(): Channel

AppointmentType::setChannel()

public function setChannel(Channel|string $channel): self

AppointmentType::getChannelValue()

public function getChannelValue(): string

AppointmentType::setChannelValue()

public function setChannelValue(?string $value): self

AppointmentType::getBookableBy()

public function getBookableBy(): BookableBy

AppointmentType::setBookableBy()

public function setBookableBy(BookableBy|string $by): self

AppointmentType::getBookableByValue()

public function getBookableByValue(): string

AppointmentType::setBookableByValue()

public function setBookableByValue(?string $value): self

AppointmentType::getMode()

public function getMode(): BookingMode

AppointmentType::setMode()

public function setMode(BookingMode|string $mode): self

AppointmentType::getModeValue()

public function getModeValue(): string

AppointmentType::setModeValue()

public function setModeValue(?string $value): self

AppointmentType::isRequest()

public function isRequest(): bool

AppointmentType::isManualConfirmation()

public function isManualConfirmation(): bool

AppointmentType::setManualConfirmation()

public function setManualConfirmation(bool $manual): self

AppointmentType::getInstructions()

public function getInstructions(): ?string

AppointmentType::setInstructions()

public function setInstructions(?string $instructions): self

AppointmentType::getPrice()

public function getPrice(): ?string

AppointmentType::setPrice()

public function setPrice(?string $price): self

AppointmentType::getMembers()

public function getMembers(): Collection

AppointmentType::addMember()

public function addMember(Member $member): self

AppointmentType::removeMember()

public function removeMember(Member $member): self

AppointmentType::isOfferedBy()

public function isOfferedBy(Member $member): bool

AppointmentType::getOffice()

public function getOffice(): ?Office

AppointmentType::setOffice()

public function setOffice(?Office $office): self

AppointmentType::isActive()

public function isActive(): bool

AppointmentType::setActive()

public function setActive(bool $active): self

AppointmentType::getPosition()

public function getPosition(): int

AppointmentType::setPosition()

public function setPosition(?int $position): self

AppointmentType::getSpan()

public function getSpan(): int

Duration plus buffer: how long the slot keeps the member busy.

Schedule class

class Schedule Schedule.php:18

When a member receives, week after week: Monday 8:30-12:00 at the main office, for these appointment types (none listed: all of theirs), valid from - until (a summer timetable, a replacement). No office: the time is for home visits or remote appointments.

Schedule::__construct()

public function __construct(?Member $member = null, int $dayOfWeek = 1, string $startsAt = '08:30', string $endsAt = '12:00', ?Office $office = null)

Schedule::__toString()

public function __toString(): string

Schedule::getId()

public function getId(): ?int

Schedule::getMember()

public function getMember(): ?Member

Schedule::setMember()

public function setMember(?Member $member): self

Schedule::getOffice()

public function getOffice(): ?Office

Schedule::setOffice()

public function setOffice(?Office $office): self

Schedule::getDayOfWeek()

public function getDayOfWeek(): int

Schedule::setDayOfWeek()

public function setDayOfWeek(?int $day): self

Schedule::getStartsAt()

public function getStartsAt(): string

Schedule::setStartsAt()

public function setStartsAt(?string $time): self

Schedule::getEndsAt()

public function getEndsAt(): string

Schedule::setEndsAt()

public function setEndsAt(?string $time): self

Schedule::getAppointmentTypes()

public function getAppointmentTypes(): Collection

Schedule::addAppointmentType()

public function addAppointmentType(AppointmentType $type): self

Schedule::removeAppointmentType()

public function removeAppointmentType(AppointmentType $type): self

Schedule::getValidFrom()

public function getValidFrom(): ?\DateTimeImmutable

Schedule::setValidFrom()

public function setValidFrom(?\DateTimeInterface $date): self

Schedule::getValidUntil()

public function getValidUntil(): ?\DateTimeImmutable

Schedule::setValidUntil()

public function setValidUntil(?\DateTimeInterface $date): self

Schedule::accepts()

public function accepts(AppointmentType $type): bool

Whether it holds that type: none listed means every type.

Schedule::appliesOn()

public function appliesOn(\DateTimeInterface $day): bool

Whether it applies on that day (its weekday, within its validity).

ServiceArea class

class ServiceArea ServiceArea.php:17

Where the office goes to its clients: a list of postcodes and towns, or a radius around one of its offices. A home visit outside every area is refused before anyone has to call back.

ServiceArea::__construct()

public function __construct(string $name = '', array $postalCodes = [], array $towns = [])

ServiceArea::__toString()

public function __toString(): string

ServiceArea::getId()

public function getId(): ?int

ServiceArea::getName()

public function getName(): string

ServiceArea::setName()

public function setName(?string $name): self

ServiceArea::getPostalCodes()

public function getPostalCodes(): array

ServiceArea::setPostalCodes()

public function setPostalCodes(?array $codes): self

ServiceArea::getTowns()

public function getTowns(): array

ServiceArea::setTowns()

public function setTowns(?array $towns): self

ServiceArea::getPostalCodesText()

public function getPostalCodesText(): string

The back office edits both lists as one line each.

ServiceArea::setPostalCodesText()

public function setPostalCodesText(?string $text): self

ServiceArea::getTownsText()

public function getTownsText(): string

ServiceArea::setTownsText()

public function setTownsText(?string $text): self

ServiceArea::getOffice()

public function getOffice(): ?Office

ServiceArea::setOffice()

public function setOffice(?Office $office): self

ServiceArea::getRadiusKm()

public function getRadiusKm(): ?float

ServiceArea::setRadiusKm()

public function setRadiusKm(?float $radius): self

ServiceArea::getMembers()

public function getMembers(): Collection

ServiceArea::addMember()

public function addMember(Member $member): self

ServiceArea::removeMember()

public function removeMember(Member $member): self

ServiceArea::isActive()

public function isActive(): bool

ServiceArea::setActive()

public function setActive(bool $active): self

ServiceArea::contains()

public function contains(?string $postalCode, ?string $town = null, ?float $latitude = null, ?float $longitude = null): bool

Whether an address is inside: its postcode listed, its town listed (accents and case aside), or within the radius of the office.

ServiceArea::distanceKm()

public static function distanceKm(float $lat1, float $lon1, float $lat2, float $lon2): float

Great-circle distance (haversine).

Base\Office\Entity\Share

AccessLog class

class AccessLog AccessLog.php:15

Who did what with a document, when, from where: each view, download, deposit, revocation - and each refusal. Rows are never edited; the document id stays when the document itself is gone.

AccessLog::__construct()

public function __construct(int $documentId, AccessAction $action, ?User $user = null, ?string $ip = null, ?string $userAgent = null, ?int $recipientId = null)

AccessLog::getId()

public function getId(): ?int

AccessLog::getDocumentId()

public function getDocumentId(): int

AccessLog::getRecipientId()

public function getRecipientId(): ?int

AccessLog::getUser()

public function getUser(): ?User

AccessLog::getUserLabel()

public function getUserLabel(): ?string

AccessLog::getAction()

public function getAction(): AccessAction

AccessLog::getIp()

public function getIp(): ?string

AccessLog::getUserAgent()

public function getUserAgent(): ?string

AccessLog::getCreatedAt()

public function getCreatedAt(): \DateTimeImmutable

Document class

class Document Document.php:23

A document kept for someone: a result, a prescription, a report, a deed.

Its file is encrypted on the private storage (libsodium secretstream, a key per file, wrapped by the master key), its title and file name are encrypted in the database; what stays readable is who it is for, who sent it, its kind, size and dates - enough for the access rules and the log, nothing of its content.

recipient is whose document it is: the client it was sent to, or the client who deposited it. confidential keeps its content from the staff who did not write it (a secretary sees that it exists, not what it says).

Document::__construct()

public function __construct(?User $recipient = null, ?User $sender = null, DocumentKind $kind = DocumentKind::OTHER)

Document::__toString()

public function __toString(): string

Document::getId()

public function getId(): ?int

Document::getRecipient()

public function getRecipient(): ?User

Document::getSender()

public function getSender(): ?User

Document::getOffice()

public function getOffice(): ?Office

Document::setOffice()

public function setOffice(?Office $office): self

Document::getTitleCipher()

public function getTitleCipher(): string

Document::setTitleCipher()

public function setTitleCipher(string $cipher): self

Document::getFilenameCipher()

public function getFilenameCipher(): ?string

Document::setFilenameCipher()

public function setFilenameCipher(?string $cipher): self

Document::getKind()

public function getKind(): DocumentKind

Document::setKind()

public function setKind(DocumentKind $kind): self

Document::getStoragePath()

public function getStoragePath(): string

Document::getWrappedKey()

public function getWrappedKey(): string

Document::getSha256()

public function getSha256(): string

Document::getSize()

public function getSize(): int

Document::getMimeType()

public function getMimeType(): string

Document::setFile()

public function setFile(string $storagePath, string $wrappedKey, string $sha256, int $size, string $mimeType): self

Set once, by the vault, after the file is written.

Document::isConfidential()

public function isConfidential(): bool

Document::setConfidential()

public function setConfidential(bool $confidential): self

Document::getContext()

public function getContext(): ?string

Document::setContext()

public function setContext(?string $context): self

Document::getExpiresAt()

public function getExpiresAt(): ?\DateTimeImmutable

Document::setExpiresAt()

public function setExpiresAt(?\DateTimeInterface $at): self

Document::getReadAt()

public function getReadAt(): ?\DateTimeImmutable

Document::markRead()

public function markRead(): self

Document::isRead()

public function isRead(): bool

Document::getRevokedAt()

public function getRevokedAt(): ?\DateTimeImmutable

Document::revoke()

public function revoke(): self

Document::isRevoked()

public function isRevoked(): bool

Document::getCreatedAt()

public function getCreatedAt(): \DateTimeImmutable

Document::isExpired()

public function isExpired(?\DateTimeInterface $now = null): bool

Document::isAvailable()

public function isAvailable(): bool

Still to be read at all: neither revoked nor expired.

Document::isImage()

public function isImage(): bool

Base\Office\Entity\Visio

Room class

class Room Room.php:19

A video room: the host (the member) and the guest (the client), opened a little before its subject's start and closed a while after its end. The guest waits in it until the host joins. What carries the call is a gateway of glitchr/omnimeet - by default from browser to browser (omnimeet/direct): the room then only relays their handshake; the room keeps which gateway opened it and its reference there. Who may enter, who is there, when it ends: all of that is judged here, whatever the gateway.

Room::__construct()

public function __construct(string $subjectKey, ?User $host, ?User $guest, \DateTimeInterface $opensAt, \DateTimeInterface $closesAt, ?string $token = null)

Room::getId()

public function getId(): ?int

Room::getToken()

public function getToken(): string

Room::getSubjectKey()

public function getSubjectKey(): string

Room::getHost()

public function getHost(): ?User

Room::setHost()

public function setHost(?User $host): self

Room::getGuest()

public function getGuest(): ?User

Room::setGuest()

public function setGuest(?User $guest): self

Room::getOpensAt()

public function getOpensAt(): \DateTimeImmutable

Room::getClosesAt()

public function getClosesAt(): \DateTimeImmutable

Room::reschedule()

public function reschedule(\DateTimeInterface $opensAt, \DateTimeInterface $closesAt): self

Room::getHostSeenAt()

public function getHostSeenAt(): ?\DateTimeImmutable

Room::getGuestSeenAt()

public function getGuestSeenAt(): ?\DateTimeImmutable

Room::getGuestWaitingSince()

public function getGuestWaitingSince(): ?\DateTimeImmutable

Room::getConnectedAt()

public function getConnectedAt(): ?\DateTimeImmutable

Room::getEndedAt()

public function getEndedAt(): ?\DateTimeImmutable

Room::getCreatedAt()

public function getCreatedAt(): \DateTimeImmutable

Room::getGateway()

public function getGateway(): ?string

Room::getReference()

public function getReference(): ?string

Room::openedOn()

public function openedOn(string $gateway, string $reference): self

Opened on a gateway: its name, and the room's reference there.

Room::isOpenedOnGateway()

public function isOpenedOnGateway(): bool

Room::isHost()

public function isHost(?User $user): bool

Room::isGuest()

public function isGuest(?User $user): bool

Room::isParticipant()

public function isParticipant(?User $user): bool

Room::isOpen()

public function isOpen(?\DateTimeInterface $now = null): bool

Room::seen()

public function seen(User $user, ?\DateTimeInterface $now = null): self

Someone said "I am here" in the last $seconds.

Room::isHostPresent()

public function isHostPresent(int $seconds = 20, ?\DateTimeInterface $now = null): bool

Room::isGuestPresent()

public function isGuestPresent(int $seconds = 20, ?\DateTimeInterface $now = null): bool

Room::isGuestWaiting()

public function isGuestWaiting(int $seconds = 20, ?\DateTimeInterface $now = null): bool

The guest is there and the host is not: the agenda says "in the waiting room".

Room::markConnected()

public function markConnected(): self

Room::end()

public function end(): self

Signal class

class Signal Signal.php:14

One message of the WebRTC handshake, from one side of a room to the other: an offer, an answer, an ICE candidate, a hang-up. Read by the other side's short polling, purged when the room closes.

const TYPES = ['offer', 'answer', 'candidate', 'bye', 'hello']

Signal::__construct()

public function __construct(Room $room, ?User $sender, string $type, string $payload)

Signal::getId()

public function getId(): ?int

Signal::getRoom()

public function getRoom(): Room

Signal::getSender()

public function getSender(): ?User

Signal::getType()

public function getType(): string

Signal::getPayload()

public function getPayload(): string

Signal::getCreatedAt()

public function getCreatedAt(): \DateTimeImmutable

Base\Office\Enum

AccessAction enum

enum AccessAction AccessAction.php:5

case VIEW = 'view'
case DOWNLOAD = 'download'
case UPLOAD = 'upload'
case REVOKE = 'revoke'
case SHARE = 'share'
case DENIED = 'denied'

Someone asked and was refused.

AccessAction::label()

public function label(): string

AppointmentSource enum

enum AppointmentSource AppointmentSource.php:5

case ONLINE = 'online'
case PHONE = 'phone'
case STAFF = 'staff'

AppointmentSource::label()

public function label(): string

AppointmentStatus enum

enum AppointmentStatus AppointmentStatus.php:5

case REQUESTED = 'requested'
case CONFIRMED = 'confirmed'
case CANCELLED = 'cancelled'
case NO_SHOW = 'no_show'
case DONE = 'done'

AppointmentStatus::isActive()

public function isActive(): bool

Holds its slot: nobody else may book it.

AppointmentStatus::label()

public function label(): string

BookableBy enum

enum BookableBy BookableBy.php:6

Who may book an appointment type online.

case EVERYONE = 'everyone'

Anyone with an account.

case KNOWN = 'known'

Clients the office already knows: an appointment with this member before (BookingPolicyInterface may say more).

case STAFF = 'staff'

Only the office books it (by phone, at the desk).

BookableBy::label()

public function label(): string

BookingMode enum

enum BookingMode BookingMode.php:5

case SLOTS = 'slots'

The client picks a free slot.

case REQUEST = 'request'

The client asks; the office fixes the time (a notary, a lawyer, home care).

BookingMode::label()

public function label(): string

Channel enum

enum Channel Channel.php:6

How an appointment takes place.

case IN_PERSON = 'in_person'
case VIDEO = 'video'
case HOME = 'home'
case PHONE = 'phone'

Channel::label()

public function label(): string

DocumentKind enum

enum DocumentKind DocumentKind.php:5

case RESULT = 'result'
case PRESCRIPTION = 'prescription'
case REPORT = 'report'
case LETTER = 'letter'
case UPLOAD = 'upload'

Deposited by the client: a prescription to bring, an insurance card.

case OTHER = 'other'

DocumentKind::label()

public function label(): string

Base\Office\Event

AppointmentEvent class

final class AppointmentEvent extends Event AppointmentEvent.php:13

Something happened to an appointment. Base\Office\Booking\BookingMailer mails the client (never the reason, never the type's name); an application may add a push, an SMS.

const BOOKED = 'office.appointment.booked'
const REQUESTED = 'office.appointment.requested'
const CONFIRMED = 'office.appointment.confirmed'
const MOVED = 'office.appointment.moved'
const CANCELLED = 'office.appointment.cancelled'
const REMINDER = 'office.appointment.reminder'
public readonly Appointment $appointment
public readonly ?string $cancelToken
public readonly array $series

the others booked with it

public readonly ?int $hoursBefore
public readonly bool $byStaff

AppointmentEvent::__construct()

public function __construct(public readonly Appointment $appointment, public readonly ?string $cancelToken = null, public readonly array $series = [], public readonly ?int $hoursBefore = null, public readonly bool $byStaff = false)
$series

the others booked with it

ClientAccountEvent class

final class ClientAccountEvent extends Event ClientAccountEvent.php:10

A client's account opened from an invitation: a regime makes its own record of them (health: the patient profile).

const CREATED = 'office.client.created'
public readonly User $user
public readonly Invitation $invitation

ClientAccountEvent::__construct()

public function __construct(public readonly User $user, public readonly Invitation $invitation)

DocumentEvent class

final class DocumentEvent extends Event DocumentEvent.php:13

A document deposited or withdrawn. office.document.deposited is what mails the recipient "a document awaits" (never its title); a regime may add its own notice (a message, a push).

const DEPOSITED = 'office.document.deposited'
const REVOKED = 'office.document.revoked'
public readonly Document $document
public readonly bool $notify

DocumentEvent::__construct()

public function __construct(public readonly Document $document, public readonly bool $notify = true)

Base\Office\Exception

BookingException class

class BookingException extends RuntimeException BookingException.php:9

A booking refused, its reason a translation key of the office domain (booking.error.<key>): slot_taken, too_late, not_bookable, outside_area.

..

BookingException::__construct()

public function __construct(string $key, readonly array $parameters = [])

BookingException::getKey()

public function getKey(): string

BookingException::getParameters()

public function getParameters(): array

CorruptedException class

class CorruptedException extends RuntimeException CorruptedException.php:6

An encrypted file or text that does not decrypt: altered, truncated, or another key's.

KeyMissingException class

class KeyMissingException extends RuntimeException KeyMissingException.php:10

No master key, or not a usable one: the vault fails closed - nothing is stored in clear, nothing is read. Set it in the secrets vault: bin/console secrets:set SHARE_MASTER_KEY (base64 of 32 random bytes).

ShareException class

class ShareException extends RuntimeException ShareException.php:6

A deposit refused: too large, a type not accepted, an empty file (share.error.<key>).

ShareException::__construct()

public function __construct(string $key, readonly array $parameters = [])

ShareException::getKey()

public function getKey(): string

ShareException::getParameters()

public function getParameters(): array

Base\Office\Form

AppointmentRequestType class

class AppointmentRequestType extends AbstractType AppointmentRequestType.php:29

The first appointment request, open to someone who has no account: what it is about (a type in request mode), with whom if they know, how to reach them, a few words - and omnibase's data-protection notice with its box to tick (Base\Form\Type\PrivacyType).

Its labels are translation keys under label_prefix ('@office.appointment_request' by default): type, choose, member, no_preference, name, email, phone, message, message_help, privacy. A regime that words them its own way gives its prefix ('@notary.request').

AppointmentRequestType::configureOptions()

public function configureOptions(OptionsResolver $resolver): void

AppointmentRequestType::buildForm()

public function buildForm(FormBuilderInterface $builder, array $options): void

Base\Office\Form\Model

AppointmentRequest class

class AppointmentRequest AppointmentRequest.php:10

What someone who has no account yet sends to ask for a first appointment.

public ?AppointmentType $type = null
public ?Member $member = null
public ?string $name = null
public ?string $email = null
public ?string $phone = null
public ?string $message = null
public ?string $website = null

Left empty by people; filled by the robots that fill every field.

AppointmentRequest::isRobot()

public function isRobot(): bool

Base\Office\Guard

Wording class

class Wording Wording.php:18

Words the communication of a regulated practice must not carry: comparison with colleagues, disparagement, the superlatives of advertising - what the notaries' règlement professionnel (art. 14.1, 14.2) and the lawyers' RIN (art. 10.2) refuse, each in its own terms. A regime cites its texts in its own guard and adds what is its alone (Wording::scan($text, $extra, $patterns)): omnibase/notary, omnibase/lawyer.

A list of expressions is a help to the person who writes, not a judge: what it finds is shown as a warning, and what it does not find is not thereby allowed.

const PATTERNS = ['le meilleur' => '\bmeilleur(?:e|s|es)?\s+(?:notaires?|etudes?|offices?|avocat(?:e|s|es)?|cabinets?|specialistes?|experts?|juristes?|services?|tarifs?|honor…

Each a regular expression on the text once lowered and stripped of its accents.

Wording::scan()

public static function scan(?string $text, array $extra = [], array $patterns = []): array
$extra

expressions of the site's own, matched as they are written (case and accents apart)

$patterns

a regime's own patterns, label => regular expression on the plain text, after the common ones

Returns

what was found, by its label

Wording::isClean()

public static function isClean(?string $text, array $extra = [], array $patterns = []): bool

Wording::plain()

public static function plain(string $text): string

Lowered, without accents nor tags, typographic apostrophes and spaces made plain.

Base\Office\Model

Slot class

final readonly class Slot Slot.php:8

A free time to book: its start and end in the office's timezone, where.

public \DateTimeImmutable $start
public \DateTimeImmutable $end
public ?Office $office

Slot::__construct()

public function __construct(public \DateTimeImmutable $start, public \DateTimeImmutable $end, public ?Office $office = null)

Slot::key()

public function key(): string

"2026-10-12T09:30" in the office's timezone: what a URL carries.

Slot::__toString()

public function __toString(): string

Base\Office\Repository

AreaRepository class

abstract class AreaRepository extends ServiceEntityRepository AreaRepository.php:22

The fields of practice of a regime (Base\Office\Entity\Area): its repository extends this one and names its entity.

class AreaRepository extends \Base\Office\Repository\AreaRepository { public function __construct(ManagerRegistry $registry) { parent::__construct($registry, Area::class); } }

AreaRepository::findActive()

public function findActive(): array

AreaRepository::findOneActiveBySlug()

public function findOneActiveBySlug(string $slug): ?Area

AreaRepository::findForMember()

public function findForMember(Member $member): array
Returns

the fields a member follows

ContactRequestRepository class

class ContactRequestRepository extends ServiceEntityRepository ContactRequestRepository.php:10

ContactRequestRepository::__construct()

public function __construct(ManagerRegistry $registry)

ContactRequestRepository::findOpen()

public function findOpen(int $limit = 5): array

ContactRequestRepository::countOpen()

public function countOpen(): int

ContactRequestRepository::purgeBefore()

public function purgeBefore(\DateTimeInterface $before): int

MemberRepository class

class MemberRepository extends ServiceEntityRepository MemberRepository.php:10

MemberRepository::__construct()

public function __construct(ManagerRegistry $registry)

MemberRepository::findVisible()

public function findVisible(): array
Returns

the team as the site shows it

MemberRepository::findBookable()

public function findBookable(): array
Returns

those appointments can be booked with online

MemberRepository::findOneVisibleBySlug()

public function findOneVisibleBySlug(string $slug): ?Member

MemberRepository::findOneByUser()

public function findOneByUser(?object $user): ?Member

OfficeRepository class

class OfficeRepository extends ServiceEntityRepository OfficeRepository.php:10

OfficeRepository::__construct()

public function __construct(ManagerRegistry $registry)

OfficeRepository::findMain()

public function findMain(): ?Office

OfficeRepository::findOrdered()

public function findOrdered(): array

WalkInRepository class

class WalkInRepository extends ServiceEntityRepository WalkInRepository.php:10

WalkInRepository::__construct()

public function __construct(ManagerRegistry $registry)

WalkInRepository::findActive()

public function findActive(): array

Base\Office\Repository\Booking

AbsenceRepository class

class AbsenceRepository extends ServiceEntityRepository AbsenceRepository.php:10

AbsenceRepository::__construct()

public function __construct(ManagerRegistry $registry)

AbsenceRepository::findOverlapping()

public function findOverlapping(\Base\Office\Entity\Member $member, \DateTimeInterface $from, \DateTimeInterface $to): array
Returns

the member's absences overlapping [from, to)

AppointmentRepository class

class AppointmentRepository extends ServiceEntityRepository AppointmentRepository.php:11

AppointmentRepository::__construct()

public function __construct(ManagerRegistry $registry)

AppointmentRepository::findActiveBetween()

public function findActiveBetween(\Base\Office\Entity\Member $member, \DateTimeInterface $from, \DateTimeInterface $to): array
Returns

the member's appointments holding a slot in [from, to)

AppointmentRepository::findBetween()

public function findBetween(\DateTimeInterface $from, \DateTimeInterface $to, ?\Base\Office\Entity\Member $member = null, bool $withCancelled = false): array
Returns

every appointment of the day(s), all members, for the agenda

AppointmentRepository::findForClient()

public function findForClient(object $client, bool $upcoming = true, int $limit = 50): array
Returns

the client's appointments, upcoming first then the past

AppointmentRepository::hasSeen()

public function hasSeen(object $client, \Base\Office\Entity\Member $member): bool

Whether the client had an appointment with that member before (BookableBy::KNOWN).

AppointmentRepository::findOneByCancelToken()

public function findOneByCancelToken(string $token): ?Appointment

AppointmentRepository::findConfirmedStartingBetween()

public function findConfirmedStartingBetween(\DateTimeInterface $from, \DateTimeInterface $to): array
Returns

confirmed ones starting within [from, to), for the reminders

AppointmentRepository::findToClose()

public function findToClose(\DateTimeInterface $before): array
Returns

past ones still CONFIRMED: to close

AppointmentRepository::findRequested()

public function findRequested(int $limit = 20): array
Returns

requests waiting for the office

AppointmentRepository::findSeries()

public function findSeries(string $series): array

AppointmentRepository::purgeEndedBefore()

public function purgeEndedBefore(\DateTimeInterface $before): int

AppointmentTypeRepository class

class AppointmentTypeRepository extends ServiceEntityRepository AppointmentTypeRepository.php:10

AppointmentTypeRepository::__construct()

public function __construct(ManagerRegistry $registry)

AppointmentTypeRepository::findForMember()

public function findForMember(\Base\Office\Entity\Member $member): array
Returns

the active types this member offers

ScheduleRepository class

class ScheduleRepository extends ServiceEntityRepository ScheduleRepository.php:10

ScheduleRepository::__construct()

public function __construct(ManagerRegistry $registry)

ScheduleRepository::findForMember()

public function findForMember(\Base\Office\Entity\Member $member): array

ServiceAreaRepository class

class ServiceAreaRepository extends ServiceEntityRepository ServiceAreaRepository.php:10

ServiceAreaRepository::__construct()

public function __construct(ManagerRegistry $registry)

ServiceAreaRepository::findActive()

public function findActive(): array

ServiceAreaRepository::findContaining()

public function findContaining(?string $postalCode, ?string $town = null, ?float $latitude = null, ?float $longitude = null): ?ServiceArea

The first active area holding that address, or null: outside every area.

Base\Office\Repository\Share

AccessLogRepository class

class AccessLogRepository extends ServiceEntityRepository AccessLogRepository.php:10

AccessLogRepository::__construct()

public function __construct(ManagerRegistry $registry)

AccessLogRepository::findLatest()

public function findLatest(int $limit = 100, ?int $documentId = null, ?object $user = null): array
Returns

newest first

AccessLogRepository::findForRecipient()

public function findForRecipient(int $recipientId, int $limit = 200): array
Returns

what was done with this person's documents

AccessLogRepository::purgeBefore()

public function purgeBefore(\DateTimeInterface $before): int

DocumentRepository class

class DocumentRepository extends ServiceEntityRepository DocumentRepository.php:10

DocumentRepository::__construct()

public function __construct(ManagerRegistry $registry)

DocumentRepository::findForRecipient()

public function findForRecipient(object $user, bool $withRevoked = false): array
Returns

the documents kept for this user, newest first

DocumentRepository::findBySender()

public function findBySender(object $user, int $limit = 50): array
Returns

the documents this user sent

DocumentRepository::countUnread()

public function countUnread(object $user): int

DocumentRepository::findGoneBefore()

public function findGoneBefore(\DateTimeInterface $before): array
Returns

expired or revoked before that date: their files go

Base\Office\Repository\Visio

RoomRepository class

class RoomRepository extends ServiceEntityRepository RoomRepository.php:10

RoomRepository::__construct()

public function __construct(ManagerRegistry $registry)

RoomRepository::findOneByToken()

public function findOneByToken(string $token): ?Room

RoomRepository::findOneBySubject()

public function findOneBySubject(string $key): ?Room

RoomRepository::findOneByReference()

public function findOneByReference(string $reference, ?string $gateway = null): ?Room

The room a gateway knows by that name (glitchr/omnimeet's reference), on that gateway when it is given.

RoomRepository::findWaiting()

public function findWaiting(int $seconds = 20): array
Returns

rooms open now whose guest waits for the host

RoomRepository::findClosedBefore()

public function findClosedBefore(\DateTimeInterface $before): array
Returns

closed rooms whose handshake can go

SignalRepository class

class SignalRepository extends ServiceEntityRepository implements SignalStoreInterface SignalRepository.php:20

The handshake's storage: omnimeet/direct's rules (Omnimeet\Direct\Signaling) keep their messages here, as Signal rows - a room named by its reference on the gateway, a participant by "u<id>" (Base\Office\Visio\Rooms::participantId()).

SignalRepository::__construct()

public function __construct(ManagerRegistry $registry)

SignalRepository::append()

public function append(string $room, string $sender, string $type, string $payload): int

SignalRepository::read()

public function read(string $room, int $after = 0, ?string $except = null, int $limit = 200): array

SignalRepository::purge()

public function purge(string $room): int

SignalRepository::findForRecipient()

public function findForRecipient(Room $room, object $user, int $after = 0): array
Returns

what the other side sent after that id

SignalRepository::purgeRoom()

public function purgeRoom(Room $room): int

SignalRepository::countForRoom()

public function countForRoom(Room $room): int

Base\Office\Security\Voter

DocumentVoter class

final class DocumentVoter extends Voter DocumentVoter.php:24

Who may see that a document exists (VIEW: its title, its date), read it (READ: open, download) or withdraw it (REVOKE):

  • the recipient sees and reads what is theirs; the sender too, and only the sender withdraws it;
  • a revoked or expired document is read by nobody;
  • anyone else only through the regime's AudienceResolverInterface - a refusal there wins over any grant. Without one, nobody else: not the site's administrators either, by design.

DocumentVoter::__construct()

public function __construct(iterable $resolvers = [])

Base\Office\Service

ClientInvitations class

class ClientInvitations ClientInvitations.php:23

Clients are invited, on omnibase's Invitations: a link mailed (the token is never stored, only its hash), valid two weeks; accepting it creates the account - the link proved the address - and attaches the appointments taken for that address before the account existed.

const DAYS = 14

ClientInvitations::__construct()

public function __construct(readonly Invitations $invitations, readonly EntityManagerInterface $entityManager, readonly MailerInterface $mailer, readonly UrlGeneratorInterface $urls, readonly TranslatorInterface $translator, readonly EventDispatcherInterface $dispatcher, readonly string $sender)

ClientInvitations::invite()

public function invite(string $email, string $name, ?string $phone = null, ?User $by = null): Invitation

ClientInvitations::find()

public function find(string $token): ?Invitation

ClientInvitations::accept()

public function accept(Invitation $invitation, string $username, string $plainPassword): User

ClientInvitations::attach()

public function attach(User $user, string $email): int

The appointments taken for that address, without an account, are theirs now.

MemberRegistry class

class MemberRegistry MemberRegistry.php:16

A member read again from their professional register through glitchr/omnistate: an RPPS number for a health professional, a bar number later - this class does not know which, ProfessionalRegistry- Interface does. What the register said is kept on the member (registryData, registryCheckedAt); what the site shows is still the office's to write.

MemberRegistry::__construct()

public function __construct(readonly EntityManagerInterface $entityManager, readonly ?object $omnistate = null)
$omnistate

wired when glitchr/omnistate is installed

MemberRegistry::isAvailable()

public function isAvailable(): bool

MemberRegistry::refresh()

public function refresh(Member $member): ?object

Ask the register; null when the member has no number or nobody knows it.

Throws

Omnistate\Exception\UnavailableException the register did not answer (not "unknown")

Throws

Omnistate\Exception\NotSupportedException no register installed knows that kind of number

MemberRegistry::snapshot()

public static function snapshot(object $professional): array

What is worth keeping of the register's answer: no raw dump.

Base\Office\Share

AudienceResolverInterface interface

interface AudienceResolverInterface AudienceResolverInterface.php:17

Who else may see or read a document, as a regime decides: health lets the care team read a patient's results (unless the patient objected) and the secretariat see that they exist; a notary's office lets the partners read the deeds. Autoconfigured (tag office.share_audience).

decide() answers true (granted), false (refused: a refusal wins over any grant) or null (no opinion).

const VIEW = 'OFFICE_DOCUMENT_VIEW'
const READ = 'OFFICE_DOCUMENT_READ'
const REVOKE = 'OFFICE_DOCUMENT_REVOKE'

AudienceResolverInterface::decide()

public function decide(string $attribute, Document $document, UserInterface $user): ?bool

Cipher class

class Cipher Cipher.php:21

libsodium, and nothing else: a file is encrypted with a key of its own (crypto_secretstream_xchacha20poly1305, chunk by chunk, authenticated - a byte changed or a chunk missing and it does not decrypt); that key is kept wrapped by a key derived from the master key (crypto_secretbox).

Short texts - a title, a file name, an appointment's reason, a message - are sealed with another derived key.

Fail closed: without a master key (32 bytes, base64, from the secrets vault) every call throws KeyMissingException. Nothing is ever stored in clear as a fallback - unlike omnibase's #[Vault], which does.

const CHUNK = 65536

Cipher::__construct()

public function __construct(?string $masterKey = null)

Cipher::generateKey()

public static function generateKey(): string

A new master key, base64: what secrets:set SHARE_MASTER_KEY should be given.

Cipher::isConfigured()

public function isConfigured(): bool

Cipher::encryptStream()

public function encryptStream($in, $out): array

Encrypts $in to $out.

Cipher::decryptStream()

public function decryptStream($in, $out, string $wrappedKey): string

Decrypts $in to $out with the file's wrapped key; throws when a chunk does not authenticate, or the stream ends before its final chunk.

Returns

the SHA-256 of what was written

Cipher::encryptText()

public function encryptText(?string $text): ?string

A short text sealed: "v1:" + base64(nonce + box). Null stays null.

Cipher::decryptText()

public function decryptText(?string $cipher): ?string

Cipher::isSealed()

public function isSealed(?string $value): bool

DocumentVault class

class DocumentVault DocumentVault.php:30

The document vault: a file in, encrypted to the private storage, a Document row and an access log line out; and back, decrypted, checked against its SHA-256, for whoever the DocumentVoter lets read it. Who may is decided before calling here: the vault does what it is told and writes down that it did.

DocumentVault::__construct()

public function __construct(readonly Cipher $cipher, readonly EntityManagerInterface $entityManager, readonly FilesystemOperator $storage, readonly EventDispatcherInterface $dispatcher, readonly RequestStack $requestStack, readonly int $maxSize = 20971520, readonly array $mimeTypes = [])

DocumentVault::isReady()

public function isReady(): bool

DocumentVault::deposit()

public function deposit(File|string $file, User $recipient, ?User $sender, string $title, DocumentKind $kind = DocumentKind::OTHER, ?string $filename = null, bool $confidential = true, ?string $context = null, ?\DateTimeInterface $expiresAt = null, ?Office $office = null, bool $notify = true): Document

Keep a file for $recipient. The cipher refuses first when there is no key: nothing is written at all.

$file

an upload, a file, or a path

DocumentVault::open()

public function open(Document $document)

The clear content, decrypted to a temporary stream and checked against its SHA-256 before anything of it is handed out.

DocumentVault::read()

public function read(Document $document): string

DocumentVault::title()

public function title(Document $document): string

DocumentVault::filename()

public function filename(Document $document): string

DocumentVault::revoke()

public function revoke(Document $document, ?User $by): void

DocumentVault::destroy()

public function destroy(Document $document): void

The file itself goes (expired, revoked long ago, the client's data erased); the log stays.

DocumentVault::log()

public function log(Document $document, AccessAction $action, ?User $user): AccessLog

ShareMailer class

final class ShareMailer ShareMailer.php:20

"A new document awaits you in your space": the recipient is told, and sent to sign in. The e-mail never holds the document, its title, its kind, nor a link that would open it without signing in - an e-mail is not a safe place for anything confidential (and for health data, not a lawful one).

ShareMailer::__construct()

public function __construct(readonly MailerInterface $mailer, readonly UrlGeneratorInterface $urls, readonly TranslatorInterface $translator, readonly string $sender, readonly string $spaceRoute)

ShareMailer::onDeposited()

public function onDeposited(DocumentEvent $event): void

Base\Office\Twig

OfficeExtension class

class OfficeExtension extends AbstractExtension OfficeExtension.php:31

The office in templates: office_main() (the main office), office_team(), office_walk_ins(), office_next_slots(member, type), office_room(appointment) (its video room, made when first asked), office_unread_documents(), office_layout(); the filters |office_document_title, |office_reason.

OfficeExtension::__construct()

public function __construct(readonly OfficeRepository $offices, readonly MemberRepository $members, readonly WalkInRepository $walkIns, readonly DocumentRepository $documents, readonly SlotFinder $slots, readonly DocumentVault $vault, readonly Cipher $cipher, readonly Rooms $rooms, readonly Compliance $compliance, readonly string $layout = 'base.html.twig', readonly ?string $spaceNav = null, readonly string $timezone = 'Europe/Paris')

OfficeExtension::getFunctions()

public function getFunctions(): array

OfficeExtension::getFilters()

public function getFilters(): array

Base\Office\Visio

Gateways class

class Gateways Gateways.php:17

The practice's video gateways, from glitchr/omnimeet: the one new rooms open on (office.visio.gateway, "direct" unless said otherwise) and the others configured, which rooms opened earlier may still be on.

Gateways::__construct()

public function __construct(readonly Registry $registry, readonly string $default = 'direct')

Gateways::name()

public function name(): string

The name of the gateway new rooms open on.

Gateways::has()

public function has(?string $name): bool

Gateways::get()

public function get(?string $name = null): GatewayInterface
Throws

InvalidConfigException when it is not configured, or its package not installed

Gateways::factory()

public function factory(?string $name = null): ?string

The factory behind a gateway's name: "direct", "jitsi"... Null when there is no such gateway.

Gateways::isDirect()

public function isDirect(?string $name = null): bool

Whether the call goes from browser to browser, its handshake relayed here.

Gateways::turn()

public function turn(?string $name = null): ?TurnCredentials

The relay of a direct gateway, as it is configured; null for any other.

RoomSubjectInterface interface

interface RoomSubjectInterface RoomSubjectInterface.php:12

What a video room is for: who hosts, who is the guest, when it starts and ends, and a key naming it ("appointment:12"). An Appointment is one; a regime may have others (a meeting of the partners with a client).

RoomSubjectInterface::getVisioHost()

public function getVisioHost(): ?User

RoomSubjectInterface::getVisioGuest()

public function getVisioGuest(): ?User

RoomSubjectInterface::getVisioStartsAt()

public function getVisioStartsAt(): ?\DateTimeImmutable

RoomSubjectInterface::getVisioEndsAt()

public function getVisioEndsAt(): ?\DateTimeImmutable

RoomSubjectInterface::getVisioKey()

public function getVisioKey(): string

Rooms class

class Rooms Rooms.php:33

The room of a subject: made the first time it is asked for, its times kept in step with the subject's (an appointment moved), opened office.visio.open_before minutes before and closed close_after minutes after the end. Closing purges the handshake.

What carries the call is a gateway of glitchr/omnimeet: the room is opened on office.visio.gateway and keeps its reference there; entering asks that gateway how (access()). The gateway is told as little as it needs - the room's own token for a key, never what the room is for, and of the people a role and an opaque identifier; the member's name unless office.visio.names says none, the client's never.

Rooms::__construct()

public function __construct(readonly RoomRepository $rooms, readonly SignalRepository $signals, readonly EntityManagerInterface $entityManager, readonly Gateways $gateways, readonly MemberRepository $members, readonly int $openBefore = 10, readonly int $closeAfter = 30, readonly string $names = 'host')

Rooms::forSubject()

public function forSubject(RoomSubjectInterface $subject): Room

Rooms::canEnter()

public function canEnter(Room $room, ?User $user, ?\DateTimeInterface $now = null): bool

A participant, while it is open.

Rooms::access()

public function access(Room $room, User $user, ?string $locale = null): Access

How this participant enters: what the room's gateway answers to Join - the direct engine and its ICE servers, a provider's frame, or an address. Asked each time: it is made for this person, now (temporary credentials, a token). Whether they may enter is canEnter()'s to say, before.

Throws

OmnimeetException when the gateway is misconfigured or refuses

Rooms::reference()

public function reference(Room $room): string

The room's name on its gateway - where omnimeet/direct keeps its handshake - opening it there when it is not yet.

Rooms::connected()

public function connected(Room $room): void

The two are talking to each other (the handshake's answer was given).

Rooms::isDirect()

public function isDirect(Room $room): bool

Whether the room's call goes from browser to browser, its handshake relayed by the site.

Rooms::meeting()

public function meeting(Room $room): Meeting

The room as glitchr/omnimeet knows a meeting: the token for a key, the window it is open in, no title.

Rooms::participant()

public function participant(Room $room, User $user, ?string $locale = null): Participant

Someone of the room as a gateway sees them: a role, "u<id>", and for the host the member's name (office.visio.names).

Rooms::participantId()

public static function participantId(User $user): string

The opaque identifier a gateway (and the handshake's storage) knows an account by.

Rooms::heartbeat()

public function heartbeat(Room $room, User $user): void

"I am here": the waiting room and the agenda read it.

Rooms::close()

public function close(Room $room): void

Rooms::purge()

public function purge(?\DateTimeInterface $now = null): int

Closed rooms' handshakes go: health:purge and office:visio:purge call it.

Signaling class

class Signaling Signaling.php:19

since omnibase/office 1.1: use Omnimeet\Direct\Signaling with Rooms::participantId() and the room's reference; removed in 2.0

The handshake under its former name and signatures (a Room, a User): the rules are omnimeet/direct's (Omnimeet\Direct\Signaling), kept here in the Signal rows.

const MAX_PAYLOAD = DirectSignaling::MAX_PAYLOAD

Signaling::__construct()

public function __construct(readonly DirectSignaling $signaling, readonly SignalRepository $signals, readonly EntityManagerInterface $entityManager)

Signaling::send()

public function send(Room $room, User $sender, string $type, string $payload): Signal

Signaling::receive()

public function receive(Room $room, User $user, int $after = 0): array

What the other side sent after $after.

TurnCredentials class

class TurnCredentials extends DirectTurnCredentials TurnCredentials.php:14

since omnibase/office 1.1: use Omnimeet\Direct\TurnCredentials, or Base\Office\Visio\Gateways::turn(); removed in 2.0

The relay's temporary credentials, under their former name: the code is omnimeet/direct's (Omnimeet\Direct\TurnCredentials), this service the relay of the practice's direct gateway (Gateways::turn()).