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é.
OfficeBundleclassA 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
OfficeLegalSectionclassThe 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
ComplianceWidgetTypeclassThe dashboard's "Compliance": the office's checks and its regime's, each with what to do when it is not met.
4 methods
ContactRequestsWidgetTypeclassThe dashboard's "Messages from the site": the contact requests not handled yet.
4 methods
TodayWidgetTypeclassThe dashboard's "Today": the day's appointments, of the member signed in or of everyone, and the requests waiting for a time.
4 methods
WaitingRoomWidgetTypeclassThe dashboard's "Waiting room": video rooms whose guest is there and whose host is not yet.
4 methods
AppointmentRequestsclassA 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
BeneficiaryProviderInterfaceinterfaceWho 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
BookerclassBooking, 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
BookingMailerclassThe 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
BookingPolicyclassWhether 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
CalendarEntriesclassAn 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
SlotFinderclassThe 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
CloseDayCommandclassThe 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
RemindCommandclassThe 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
ComplianceclassEvery check, run: the widget's list.
2 methods
ComplianceCheckInterfaceinterfaceOne 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
ComplianceResultclass3 methods · 5 properties · 3 constants
LegalNoticeCheckclassThe legal notice names the host (and, in the settings, who to write to about one's data).
2 methods
TurnCheckclassThe 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
VaultKeyCheckclass2 methods
AdminPageTraittraitA 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
AgendaControllerclassThe 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
DocumentsControllerclassThe 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
OpenToTraittraitThe 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
AbsenceCrudControllerclassA member away: their slots are gone for that time.
6 methods
AppointmentCrudControllerclassEvery 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
AppointmentTypeCrudControllerclassWhat an appointment is for: duration, buffer, channel, who may book it online, by slot or by request.
6 methods
ContactRequestCrudControllerclassThe contact form's messages, read and marked handled; purged after office.contact.retention_months.
6 methods
MemberCrudControllerclassThe 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
OfficeCrudControllerclassThe office's places: address, phones, access, accessibility. The opening hours are omnibase's (the back office's hours screen).
6 methods
ScheduleCrudControllerclassThe weekly schedules: who receives when, where, for which types.
6 methods
ServiceAreaCrudControllerclassWhere the office goes to its clients: postcodes, towns, or a radius.
5 methods
WalkInCrudControllerclassHours without an appointment: the blood tests at 7:30, a walk-in morning.
5 methods
AbstractRequestControllerclassThe 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
BookingControllerclassBooking 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
InvitationControllerclassThe invitation's link: choose an identifier and a password, the account is opened.
1 method
OfficeControllerclassThe 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
RequestControllerclassThe 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
ShareControllerclassThe 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
VisioControllerclassThe 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
OfficeConfigurationclass1 method
OfficeExtensionclass4 methods · 1 constant
VocabularyclassHow 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
VisioGatewayPassclassAn 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
AreaclassA 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
ContactRequestclassA 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
InvitationclassA 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
MemberclassSomeone 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
OfficeclassA 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
WalkInclassHours 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
AbsenceclassA 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
AppointmentclassAn 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
AppointmentTypeclassWhat 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
ScheduleclassWhen 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
ServiceAreaclassWhere 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
AccessLogclassWho 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
DocumentclassA document kept for someone: a result, a prescription, a report, a deed.
35 methods
RoomclassA 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
SignalclassOne 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
AccessActionenum1 method · 6 cases
AppointmentSourceenum1 method · 3 cases
AppointmentStatusenum2 methods · 5 cases
BookableByenumWho may book an appointment type online.
1 method · 3 cases
BookingModeenum1 method · 2 cases
ChannelenumHow an appointment takes place.
1 method · 4 cases
DocumentKindenum1 method · 6 cases
AppointmentEventclassSomething 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
ClientAccountEventclassA 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
DocumentEventclassA 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
BookingExceptionclassA booking refused, its reason a translation key of the office domain (booking.error.<key>): slot_taken, too_late, not_bookable, outside_area.
3 methods
CorruptedExceptionclassAn encrypted file or text that does not decrypt: altered, truncated, or another key's.
KeyMissingExceptionclassNo 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).
ShareExceptionclassA deposit refused: too large, a type not accepted, an empty file (share.error.<key>).
3 methods
AppointmentRequestTypeclassThe 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
AppointmentRequestclassWhat someone who has no account yet sends to ask for a first appointment.
1 method · 7 properties
WordingclassWords 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
SlotclassA free time to book: its start and end in the office's timezone, where.
3 methods · 3 properties
AreaRepositoryclassThe fields of practice of a regime (Base\Office\Entity\Area): its repository extends this one and names its entity.
3 methods
ContactRequestRepositoryclass4 methods
MemberRepositoryclass5 methods
OfficeRepositoryclass3 methods
WalkInRepositoryclass2 methods
Base\Office\Repository\Booking
AbsenceRepositoryclass2 methods
AppointmentRepositoryclass11 methods
AppointmentTypeRepositoryclass2 methods
ScheduleRepositoryclass2 methods
ServiceAreaRepositoryclass3 methods
AccessLogRepositoryclass4 methods
DocumentRepositoryclass5 methods
RoomRepositoryclass6 methods
SignalRepositoryclassThe 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
DocumentVoterclassWho may see that a document exists (VIEW: its title, its date), read it (READ: open, download) or withdraw it (REVOKE):
1 method
ClientInvitationsclassClients 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
MemberRegistryclassA 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
AudienceResolverInterfaceinterfaceWho 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
Cipherclasslibsodium, 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
DocumentVaultclassThe 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
ShareMailerclass"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
OfficeExtensionclassThe 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
GatewaysclassThe 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
RoomSubjectInterfaceinterfaceWhat 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
RoomsclassThe 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
SignalingclassThe 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
TurnCredentialsclassThe 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(): stringModern layout: the class lives in src/, the bundle root is the package root.
OfficeBundle::build()
public function build(ContainerBuilder $container): voidBase\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(): stringOfficeLegalSection::getFields()
public function getFields(): arrayBase\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(): stringComplianceWidgetType::getTemplate()
public function getTemplate(): stringComplianceWidgetType::getTemplateVars()
public function getTemplateVars(MenuItem $widget): arrayContactRequestsWidgetType 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(): stringContactRequestsWidgetType::getTemplate()
public function getTemplate(): stringContactRequestsWidgetType::getTemplateVars()
public function getTemplateVars(MenuItem $widget): arrayTodayWidgetType 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(): stringTodayWidgetType::getTemplate()
public function getTemplate(): stringTodayWidgetType::getTemplateVars()
public function getTemplateVars(MenuItem $widget): arrayWaitingRoomWidgetType 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(): stringWaitingRoomWidgetType::getTemplate()
public function getTemplate(): stringWaitingRoomWidgetType::getTemplateVars()
public function getTemplateVars(MenuItem $widget): arrayBase\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(): arrayWhat 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 = ''): AppointmentRequestThe 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): ?MemberThe member asked for when they offer that type, else the first who does.
AppointmentRequests::send()
public function send(AppointmentRequest $request, ?UserInterface $user = null): AppointmentRegisters 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 = []): AppointmentA 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 = []): AppointmentA 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): AppointmentA 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): AppointmentMoved by the staff: only another appointment can stop it.
Booker::cancel()
public function cancel(Appointment $appointment, bool $byStaff = false, ?\DateTimeInterface $now = null): voidCancelled: 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 = []): arrayA 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): ?stringBookingMailer 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): voidBookingMailer::onRequested()
public function onRequested(AppointmentEvent $event): voidBookingMailer::onConfirmed()
public function onConfirmed(AppointmentEvent $event): voidBookingMailer::onMoved()
public function onMoved(AppointmentEvent $event): voidBookingMailer::onCancelled()
public function onCancelled(AppointmentEvent $event): voidBookingMailer::onReminder()
public function onReminder(AppointmentEvent $event): voidBookingPolicy 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): boolBookingPolicy::isListed()
public function isListed(AppointmentType $type, Member $member): boolWhether 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): ?CalendarEntrySlotFinder 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(): \DateTimeZoneSlotFinder::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): boolWhether 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): arrayThe computation itself, on what was read: testable without a database.
$busyheld 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(): arrayComplianceCheckInterface 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(): ComplianceResultComplianceResult class
final readonly class ComplianceResult ComplianceResult.php:5
const OK = 'ok'const WARNING = 'warning'const MISSING = 'missing'public string $labelA translation key of the domain given (its label).
public string $statuspublic ?string $adviceWhat to do about it, a translation key too; null when all is well.
public string $domainpublic 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'): selfComplianceResult::isOk()
public function isOk(): boolLegalNoticeCheck 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(): ComplianceResultTurnCheck 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(): ComplianceResultTurnCheck::reachable()
public static function reachable(string $url, float $timeout = 1.0): boolA 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(): ComplianceResultBase\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): voidAgendaController 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 = 7const 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): ResponseAgendaController::book()
public function book(Request $request, ClientInvitations $invitations): ResponseThe quick booking: taken on the phone or at the desk.
AgendaController::action()
public function action(Request $request, Appointment $appointment, string $action): Responsedone | 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): ResponseDocumentsController::send()
public function send(Request $request): ResponseDocumentsController::revoke()
public function revoke(Request $request, int $id): ResponseDocumentsController::log()
public function log(Request $request, AccessLogRepository $logs): ResponseWho 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): voidAbsenceCrudController::getEntityFqcn()
public static function getEntityFqcn(): stringAbsenceCrudController::getPreferredIcon()
public static function getPreferredIcon(): ?stringAbsenceCrudController::configureCrud()
public function configureCrud(Crud $crud): CrudAbsenceCrudController::configureActions()
public function configureActions(Actions $actions): ActionsAbsenceCrudController::configureFields()
public function configureFields(string $pageName): iterableAppointmentCrudController 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): voidAppointmentCrudController::getEntityFqcn()
public static function getEntityFqcn(): stringAppointmentCrudController::getPreferredIcon()
public static function getPreferredIcon(): ?stringAppointmentCrudController::configureCrud()
public function configureCrud(Crud $crud): CrudAppointmentCrudController::configureActions()
public function configureActions(Actions $actions): ActionsAppointmentCrudController::configureFields()
public function configureFields(string $pageName): iterableAppointmentTypeCrudController 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): voidAppointmentTypeCrudController::getEntityFqcn()
public static function getEntityFqcn(): stringAppointmentTypeCrudController::getPreferredIcon()
public static function getPreferredIcon(): ?stringAppointmentTypeCrudController::configureCrud()
public function configureCrud(Crud $crud): CrudAppointmentTypeCrudController::configureActions()
public function configureActions(Actions $actions): ActionsAppointmentTypeCrudController::configureFields()
public function configureFields(string $pageName): iterableContactRequestCrudController 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): voidContactRequestCrudController::getEntityFqcn()
public static function getEntityFqcn(): stringContactRequestCrudController::getPreferredIcon()
public static function getPreferredIcon(): ?stringContactRequestCrudController::configureCrud()
public function configureCrud(Crud $crud): CrudContactRequestCrudController::configureActions()
public function configureActions(Actions $actions): ActionsContactRequestCrudController::configureFields()
public function configureFields(string $pageName): iterableMemberCrudController 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): voidMemberCrudController::getEntityFqcn()
public static function getEntityFqcn(): stringMemberCrudController::getPreferredIcon()
public static function getPreferredIcon(): ?stringMemberCrudController::configureCrud()
public function configureCrud(Crud $crud): CrudMemberCrudController::configureActions()
public function configureActions(Actions $actions): ActionsMemberCrudController::configureFields()
public function configureFields(string $pageName): iterableMemberCrudController::setRegistry()
public function setRegistry(MemberRegistry $registry): voidMemberCrudController::registry()
public function registry(Request $request, string $entityId): ResponseThe 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): voidOfficeCrudController::getEntityFqcn()
public static function getEntityFqcn(): stringOfficeCrudController::getPreferredIcon()
public static function getPreferredIcon(): ?stringOfficeCrudController::configureCrud()
public function configureCrud(Crud $crud): CrudOfficeCrudController::configureActions()
public function configureActions(Actions $actions): ActionsOfficeCrudController::configureFields()
public function configureFields(string $pageName): iterableScheduleCrudController 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): voidScheduleCrudController::getEntityFqcn()
public static function getEntityFqcn(): stringScheduleCrudController::getPreferredIcon()
public static function getPreferredIcon(): ?stringScheduleCrudController::configureCrud()
public function configureCrud(Crud $crud): CrudScheduleCrudController::configureActions()
public function configureActions(Actions $actions): ActionsScheduleCrudController::configureFields()
public function configureFields(string $pageName): iterableServiceAreaCrudController 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): voidServiceAreaCrudController::getEntityFqcn()
public static function getEntityFqcn(): stringServiceAreaCrudController::getPreferredIcon()
public static function getPreferredIcon(): ?stringServiceAreaCrudController::configureActions()
public function configureActions(Actions $actions): ActionsServiceAreaCrudController::configureFields()
public function configureFields(string $pageName): iterableWalkInCrudController 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): voidWalkInCrudController::getEntityFqcn()
public static function getEntityFqcn(): stringWalkInCrudController::getPreferredIcon()
public static function getPreferredIcon(): ?stringWalkInCrudController::configureActions()
public function configureActions(Actions $actions): ActionsWalkInCrudController::configureFields()
public function configureFields(string $pageName): iterableBase\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): voidBookingController 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(): ResponseBookingController::member()
public function member(Member $member): ResponseBookingController::slots()
public function slots(Request $request, Member $member, AppointmentType $type): ResponseBookingController::confirm()
public function confirm(Request $request, Member $member, AppointmentType $type, string $start): ResponseBookingController::done()
public function done(Appointment $appointment, CalendarEntries $entries, GoogleCalendarLink $google): ResponseBookingController::ics()
public function ics(Appointment $appointment, CalendarEntries $entries, Ics $ics, Request $request): ResponseBookingController::cancel()
public function cancel(Request $request, string $token): ResponseThe link of the e-mail: no sign-in needed, the token is the proof.
BookingController::space()
public function space(): ResponseBookingController::spaceCancel()
public function spaceCancel(Request $request, Appointment $appointment): ResponseInvitationController 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): ResponseOfficeController 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(): ResponseOfficeController::member()
public function member(string $slug): ResponseOfficeController::hours()
public function hours(WalkInRepository $walkIns): ResponseOfficeController::contact()
public function contact(Request $request, FormFactoryInterface $forms, EntityManagerInterface $entityManager, MailerInterface $mailer, string $recipient, string $sender, string $notice): ResponseRequestController 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): ResponseShareController 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(): ResponseShareController::download()
public function download(Request $request, int $id): ResponseShareController::deposit()
public function deposit(Request $request): ResponseA 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): ResponseVisioController 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): ResponseVisioController::engine()
public function engine(string $token): ResponseThe 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): JsonResponseVisioController::send()
public function send(Request $request, string $token): JsonResponseVisioController::poll()
public function poll(Request $request, string $token): JsonResponseThe other side's messages after after, and who is there.
VisioController::end()
public function end(Request $request, string $token): ResponseVisioController::document()
public function document(Request $request, string $token, DocumentVault $vault): JsonResponseA 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(): TreeBuilderOfficeExtension 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): OfficeConfigurationOfficeExtension::visioAliases()
public static function visioAliases(array $configs): arrayThe 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.
$configsoffice's configurations, unprocessed
- Returns
the options, and the aliases a file still sets
OfficeExtension::prepend()
public function prepend(ContainerBuilder $container): voidThe 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): voidVocabulary 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$practicea 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): voidBase\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(): stringArea::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): staticArea::getItemsText()
public function getItemsText(): stringThe list, one a line: what the back office's form edits.
Area::setItemsText()
public function setItemsText(?string $text): staticArea::getId()
public function getId(): ?intArea::getName()
public function getName(): stringArea::setName()
public function setName(?string $name): staticArea::getSlug()
public function getSlug(): stringArea::setSlug()
public function setSlug(?string $slug): staticArea::getSummary()
public function getSummary(): ?stringArea::setSummary()
public function setSummary(?string $summary): staticArea::getBody()
public function getBody(): ?stringArea::setBody()
public function setBody(?string $body): staticArea::getMembers()
public function getMembers(): CollectionArea::addMember()
public function addMember(Member $member): staticArea::removeMember()
public function removeMember(Member $member): staticArea::isActive()
public function isActive(): boolArea::setActive()
public function setActive(bool $active): staticArea::getPosition()
public function getPosition(): intArea::setPosition()
public function setPosition(?int $position): staticArea::getParagraphs()
public function getParagraphs(): array- Returns
the body's paragraphs
Area::getPublicText()
public function getPublicText(): stringEverything 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(): stringContactRequest::getId()
public function getId(): ?intContactRequest::getName()
public function getName(): stringContactRequest::getEmail()
public function getEmail(): stringContactRequest::getPhone()
public function getPhone(): ?stringContactRequest::getMessage()
public function getMessage(): stringContactRequest::getCreatedAt()
public function getCreatedAt(): \DateTimeImmutableContactRequest::getHandledAt()
public function getHandledAt(): ?\DateTimeImmutableContactRequest::isHandled()
public function isHandled(): boolContactRequest::setHandled()
public function setHandled(bool $handled): selfInvitation 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(): ?intInvitation::getPhone()
public function getPhone(): ?stringInvitation::setPhone()
public function setPhone(?string $phone): staticMember 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(): stringMember::getId()
public function getId(): ?intMember::getUser()
public function getUser(): ?UserMember::setUser()
public function setUser(?User $user): selfMember::getSlug()
public function getSlug(): stringMember::setSlug()
public function setSlug(?string $slug): selfMember::getDisplayName()
public function getDisplayName(): stringMember::setDisplayName()
public function setDisplayName(?string $name): selfMember::getTitle()
public function getTitle(): ?stringMember::setTitle()
public function setTitle(?string $title): selfMember::getCategory()
public function getCategory(): ?stringMember::setCategory()
public function setCategory(?string $category): selfMember::getPortrait()
public function getPortrait(): ?stringMember::setPortrait()
public function setPortrait(?string $portrait): selfMember::getLanguages()
public function getLanguages(): arrayMember::setLanguages()
public function setLanguages(?array $languages): selfMember::getBiography()
public function getBiography(): ?stringMember::setBiography()
public function setBiography(?string $biography): selfMember::getRegistryId()
public function getRegistryId(): ?stringMember::setRegistryId()
public function setRegistryId(?string $registryId): selfMember::getRegistryCheckedAt()
public function getRegistryCheckedAt(): ?\DateTimeImmutableMember::getRegistryData()
public function getRegistryData(): ?arrayMember::setRegistryData()
public function setRegistryData(?array $data, ?\DateTimeImmutable $checkedAt = null): selfMember::getOffices()
public function getOffices(): CollectionMember::addOffice()
public function addOffice(Office $office): selfMember::removeOffice()
public function removeOffice(Office $office): selfMember::getColor()
public function getColor(): ?stringMember::setColor()
public function setColor(?string $color): selfMember::isVisible()
public function isVisible(): boolMember::setVisible()
public function setVisible(bool $visible): selfMember::isActive()
public function isActive(): boolMember::setActive()
public function setActive(bool $active): selfMember::isBookable()
public function isBookable(): boolMember::setBookable()
public function setBookable(bool $bookable): selfMember::getPosition()
public function getPosition(): intMember::setPosition()
public function setPosition(?int $position): selfMember::getInitials()
public function getInitials(): stringThe 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(): stringOffice::slugify()
public static function slugify(string $text): stringOffice::getId()
public function getId(): ?intOffice::getName()
public function getName(): stringOffice::setName()
public function setName(?string $name): selfOffice::getSlug()
public function getSlug(): stringOffice::setSlug()
public function setSlug(?string $slug): selfOffice::getStreet()
public function getStreet(): ?stringOffice::setStreet()
public function setStreet(?string $street): selfOffice::getPostalCode()
public function getPostalCode(): ?stringOffice::setPostalCode()
public function setPostalCode(?string $postalCode): selfOffice::getCity()
public function getCity(): ?stringOffice::setCity()
public function setCity(?string $city): selfOffice::getCountry()
public function getCountry(): stringOffice::setCountry()
public function setCountry(?string $country): selfOffice::getLatitude()
public function getLatitude(): ?floatOffice::setLatitude()
public function setLatitude(?float $latitude): selfOffice::getLongitude()
public function getLongitude(): ?floatOffice::setLongitude()
public function setLongitude(?float $longitude): selfOffice::getPhone()
public function getPhone(): ?stringOffice::setPhone()
public function setPhone(?string $phone): selfOffice::getSecondaryPhone()
public function getSecondaryPhone(): ?stringOffice::setSecondaryPhone()
public function setSecondaryPhone(?string $phone): selfOffice::getSecondaryPhoneLabel()
public function getSecondaryPhoneLabel(): ?stringOffice::setSecondaryPhoneLabel()
public function setSecondaryPhoneLabel(?string $label): selfOffice::getEmail()
public function getEmail(): ?stringOffice::setEmail()
public function setEmail(?string $email): selfOffice::getAccess()
public function getAccess(): ?stringOffice::setAccess()
public function setAccess(?string $access): selfOffice::isWheelchairAccessible()
public function isWheelchairAccessible(): boolOffice::setWheelchairAccessible()
public function setWheelchairAccessible(bool $accessible): selfOffice::getAccessibility()
public function getAccessibility(): ?stringOffice::setAccessibility()
public function setAccessibility(?string $accessibility): selfOffice::getParking()
public function getParking(): ?stringOffice::setParking()
public function setParking(?string $parking): selfOffice::getLanguages()
public function getLanguages(): arrayOffice::setLanguages()
public function setLanguages(?array $languages): selfOffice::isMain()
public function isMain(): boolOffice::setMain()
public function setMain(bool $main): selfOffice::getPosition()
public function getPosition(): intOffice::setPosition()
public function setPosition(?int $position): selfOffice::getAddress()
public function getAddress(): string"12 rue des Tilleuls, 67000 Strasbourg"
Office::toSchema()
public function toSchema(): arrayschema.org PostalAddress.
Office::getMapUrl()
public function getMapUrl(): ?stringAn 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(): stringWalkIn::getId()
public function getId(): ?intWalkIn::getLabel()
public function getLabel(): stringWalkIn::setLabel()
public function setLabel(?string $label): selfWalkIn::getDays()
public function getDays(): arrayWalkIn::setDays()
public function setDays(?array $days): selfWalkIn::getOpensAt()
public function getOpensAt(): stringWalkIn::setOpensAt()
public function setOpensAt(?string $time): selfWalkIn::getClosesAt()
public function getClosesAt(): stringWalkIn::setClosesAt()
public function setClosesAt(?string $time): selfWalkIn::getOffice()
public function getOffice(): ?OfficeWalkIn::setOffice()
public function setOffice(?Office $office): selfWalkIn::getMember()
public function getMember(): ?MemberWalkIn::setMember()
public function setMember(?Member $member): selfWalkIn::getNotes()
public function getNotes(): ?stringWalkIn::setNotes()
public function setNotes(?string $notes): selfWalkIn::isActive()
public function isActive(): boolWalkIn::setActive()
public function setActive(bool $active): selfWalkIn::isOn()
public function isOn(int $isoDay): boolBase\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(): stringAbsence::getId()
public function getId(): ?intAbsence::getMember()
public function getMember(): ?MemberAbsence::setMember()
public function setMember(?Member $member): selfAbsence::getStartsAt()
public function getStartsAt(): \DateTimeImmutableAbsence::setStartsAt()
public function setStartsAt(?\DateTimeInterface $at): selfAbsence::getEndsAt()
public function getEndsAt(): \DateTimeImmutableAbsence::setEndsAt()
public function setEndsAt(?\DateTimeInterface $at): selfAbsence::getReason()
public function getReason(): ?stringAbsence::setReason()
public function setReason(?string $reason): selfAbsence::overlaps()
public function overlaps(\DateTimeInterface $start, \DateTimeInterface $end): boolAppointment 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(): stringAppointment::getId()
public function getId(): ?intAppointment::getMember()
public function getMember(): ?MemberAppointment::setMember()
public function setMember(?Member $member): selfAppointment::getType()
public function getType(): ?AppointmentTypeAppointment::setType()
public function setType(?AppointmentType $type): selfAppointment::getOffice()
public function getOffice(): ?OfficeAppointment::setOffice()
public function setOffice(?Office $office): selfAppointment::getAddress()
public function getAddress(): ?stringAppointment::setAddress()
public function setAddress(?string $address): selfAppointment::getStartsAt()
public function getStartsAt(): ?\DateTimeImmutableAppointment::getEndsAt()
public function getEndsAt(): ?\DateTimeImmutableAppointment::setStartsAt()
public function setStartsAt(?\DateTimeInterface $at): selfThe start, and the end from the type's duration.
Appointment::setEndsAt()
public function setEndsAt(?\DateTimeInterface $at): selfAppointment::getClient()
public function getClient(): ?UserAppointment::setClient()
public function setClient(?User $client): selfAppointment::getClientName()
public function getClientName(): ?stringAppointment::setClientName()
public function setClientName(?string $name): selfAppointment::getClientEmail()
public function getClientEmail(): ?stringAppointment::setClientEmail()
public function setClientEmail(?string $email): selfAppointment::getClientPhone()
public function getClientPhone(): ?stringAppointment::setClientPhone()
public function setClientPhone(?string $phone): selfAppointment::getBeneficiary()
public function getBeneficiary(): ?stringAppointment::setBeneficiary()
public function setBeneficiary(?string $beneficiary): selfAppointment::getStatus()
public function getStatus(): AppointmentStatusAppointment::setStatus()
public function setStatus(AppointmentStatus|string $status): selfAppointment::getStatusValue()
public function getStatusValue(): stringAppointment::setStatusValue()
public function setStatusValue(?string $value): selfAppointment::isActive()
public function isActive(): boolAppointment::isCancelled()
public function isCancelled(): boolAppointment::getSource()
public function getSource(): AppointmentSourceAppointment::setSource()
public function setSource(AppointmentSource|string $source): selfAppointment::getReasonCipher()
public function getReasonCipher(): ?stringAppointment::setReasonCipher()
public function setReasonCipher(?string $cipher): selfAppointment::getSlotKey()
public function getSlotKey(): ?stringAppointment::getCancelTokenHash()
public function getCancelTokenHash(): ?stringAppointment::setCancelTokenHash()
public function setCancelTokenHash(?string $hash): selfAppointment::getRemindersSent()
public function getRemindersSent(): arrayAppointment::markReminderSent()
public function markReminderSent(int $hours): selfAppointment::hasReminderBeenSent()
public function hasReminderBeenSent(int $hours): boolAppointment::resetReminders()
public function resetReminders(): selfAppointment::getSeries()
public function getSeries(): ?stringAppointment::setSeries()
public function setSeries(?string $series): selfAppointment::getMeta()
public function getMeta(): arrayAppointment::getMetaValue()
public function getMetaValue(string $key, mixed $default = null): mixedAppointment::setMetaValue()
public function setMetaValue(string $key, mixed $value): selfAppointment::getCreatedBy()
public function getCreatedBy(): ?UserAppointment::setCreatedBy()
public function setCreatedBy(?User $user): selfAppointment::getCreatedAt()
public function getCreatedAt(): \DateTimeImmutableAppointment::getCancelledAt()
public function getCancelledAt(): ?\DateTimeImmutableAppointment::getChannel()
public function getChannel(): ChannelAppointment::isVideo()
public function isVideo(): boolAppointment::isUpcoming()
public function isUpcoming(?\DateTimeInterface $now = null): boolAppointment::slotKeyFor()
public static function slotKeyFor(Member $member, \DateTimeInterface $start): stringThe slot key: who and when, the same text for the same slot whatever the timezone it was written in.
Appointment::getVisioHost()
public function getVisioHost(): ?UserAppointment::getVisioGuest()
public function getVisioGuest(): ?UserAppointment::getVisioStartsAt()
public function getVisioStartsAt(): ?\DateTimeImmutableAppointment::getVisioEndsAt()
public function getVisioEndsAt(): ?\DateTimeImmutableAppointment::getVisioKey()
public function getVisioKey(): stringAppointmentType 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(): stringAppointmentType::getId()
public function getId(): ?intAppointmentType::getName()
public function getName(): stringAppointmentType::setName()
public function setName(?string $name): selfAppointmentType::getSlug()
public function getSlug(): stringAppointmentType::setSlug()
public function setSlug(?string $slug): selfAppointmentType::getDuration()
public function getDuration(): intAppointmentType::setDuration()
public function setDuration(?int $duration): selfAppointmentType::getBuffer()
public function getBuffer(): intAppointmentType::setBuffer()
public function setBuffer(?int $buffer): selfAppointmentType::getChannel()
public function getChannel(): ChannelAppointmentType::setChannel()
public function setChannel(Channel|string $channel): selfAppointmentType::getChannelValue()
public function getChannelValue(): stringAppointmentType::setChannelValue()
public function setChannelValue(?string $value): selfAppointmentType::getBookableBy()
public function getBookableBy(): BookableByAppointmentType::setBookableBy()
public function setBookableBy(BookableBy|string $by): selfAppointmentType::getBookableByValue()
public function getBookableByValue(): stringAppointmentType::setBookableByValue()
public function setBookableByValue(?string $value): selfAppointmentType::getMode()
public function getMode(): BookingModeAppointmentType::setMode()
public function setMode(BookingMode|string $mode): selfAppointmentType::getModeValue()
public function getModeValue(): stringAppointmentType::setModeValue()
public function setModeValue(?string $value): selfAppointmentType::isRequest()
public function isRequest(): boolAppointmentType::isManualConfirmation()
public function isManualConfirmation(): boolAppointmentType::setManualConfirmation()
public function setManualConfirmation(bool $manual): selfAppointmentType::getInstructions()
public function getInstructions(): ?stringAppointmentType::setInstructions()
public function setInstructions(?string $instructions): selfAppointmentType::getPrice()
public function getPrice(): ?stringAppointmentType::setPrice()
public function setPrice(?string $price): selfAppointmentType::getMembers()
public function getMembers(): CollectionAppointmentType::addMember()
public function addMember(Member $member): selfAppointmentType::removeMember()
public function removeMember(Member $member): selfAppointmentType::isOfferedBy()
public function isOfferedBy(Member $member): boolAppointmentType::getOffice()
public function getOffice(): ?OfficeAppointmentType::setOffice()
public function setOffice(?Office $office): selfAppointmentType::isActive()
public function isActive(): boolAppointmentType::setActive()
public function setActive(bool $active): selfAppointmentType::getPosition()
public function getPosition(): intAppointmentType::setPosition()
public function setPosition(?int $position): selfAppointmentType::getSpan()
public function getSpan(): intDuration 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(): stringSchedule::getId()
public function getId(): ?intSchedule::getMember()
public function getMember(): ?MemberSchedule::setMember()
public function setMember(?Member $member): selfSchedule::getOffice()
public function getOffice(): ?OfficeSchedule::setOffice()
public function setOffice(?Office $office): selfSchedule::getDayOfWeek()
public function getDayOfWeek(): intSchedule::setDayOfWeek()
public function setDayOfWeek(?int $day): selfSchedule::getStartsAt()
public function getStartsAt(): stringSchedule::setStartsAt()
public function setStartsAt(?string $time): selfSchedule::getEndsAt()
public function getEndsAt(): stringSchedule::setEndsAt()
public function setEndsAt(?string $time): selfSchedule::getAppointmentTypes()
public function getAppointmentTypes(): CollectionSchedule::addAppointmentType()
public function addAppointmentType(AppointmentType $type): selfSchedule::removeAppointmentType()
public function removeAppointmentType(AppointmentType $type): selfSchedule::getValidFrom()
public function getValidFrom(): ?\DateTimeImmutableSchedule::setValidFrom()
public function setValidFrom(?\DateTimeInterface $date): selfSchedule::getValidUntil()
public function getValidUntil(): ?\DateTimeImmutableSchedule::setValidUntil()
public function setValidUntil(?\DateTimeInterface $date): selfSchedule::accepts()
public function accepts(AppointmentType $type): boolWhether it holds that type: none listed means every type.
Schedule::appliesOn()
public function appliesOn(\DateTimeInterface $day): boolWhether 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(): stringServiceArea::getId()
public function getId(): ?intServiceArea::getName()
public function getName(): stringServiceArea::setName()
public function setName(?string $name): selfServiceArea::getPostalCodes()
public function getPostalCodes(): arrayServiceArea::setPostalCodes()
public function setPostalCodes(?array $codes): selfServiceArea::getTowns()
public function getTowns(): arrayServiceArea::setTowns()
public function setTowns(?array $towns): selfServiceArea::getPostalCodesText()
public function getPostalCodesText(): stringThe back office edits both lists as one line each.
ServiceArea::setPostalCodesText()
public function setPostalCodesText(?string $text): selfServiceArea::getTownsText()
public function getTownsText(): stringServiceArea::setTownsText()
public function setTownsText(?string $text): selfServiceArea::getOffice()
public function getOffice(): ?OfficeServiceArea::setOffice()
public function setOffice(?Office $office): selfServiceArea::getRadiusKm()
public function getRadiusKm(): ?floatServiceArea::setRadiusKm()
public function setRadiusKm(?float $radius): selfServiceArea::getMembers()
public function getMembers(): CollectionServiceArea::addMember()
public function addMember(Member $member): selfServiceArea::removeMember()
public function removeMember(Member $member): selfServiceArea::isActive()
public function isActive(): boolServiceArea::setActive()
public function setActive(bool $active): selfServiceArea::contains()
public function contains(?string $postalCode, ?string $town = null, ?float $latitude = null, ?float $longitude = null): boolWhether 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): floatGreat-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(): ?intAccessLog::getDocumentId()
public function getDocumentId(): intAccessLog::getRecipientId()
public function getRecipientId(): ?intAccessLog::getUser()
public function getUser(): ?UserAccessLog::getUserLabel()
public function getUserLabel(): ?stringAccessLog::getAction()
public function getAction(): AccessActionAccessLog::getIp()
public function getIp(): ?stringAccessLog::getUserAgent()
public function getUserAgent(): ?stringAccessLog::getCreatedAt()
public function getCreatedAt(): \DateTimeImmutableDocument 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(): stringDocument::getId()
public function getId(): ?intDocument::getRecipient()
public function getRecipient(): ?UserDocument::getSender()
public function getSender(): ?UserDocument::getOffice()
public function getOffice(): ?OfficeDocument::setOffice()
public function setOffice(?Office $office): selfDocument::getTitleCipher()
public function getTitleCipher(): stringDocument::setTitleCipher()
public function setTitleCipher(string $cipher): selfDocument::getFilenameCipher()
public function getFilenameCipher(): ?stringDocument::setFilenameCipher()
public function setFilenameCipher(?string $cipher): selfDocument::getKind()
public function getKind(): DocumentKindDocument::setKind()
public function setKind(DocumentKind $kind): selfDocument::getStoragePath()
public function getStoragePath(): stringDocument::getWrappedKey()
public function getWrappedKey(): stringDocument::getSha256()
public function getSha256(): stringDocument::getSize()
public function getSize(): intDocument::getMimeType()
public function getMimeType(): stringDocument::setFile()
public function setFile(string $storagePath, string $wrappedKey, string $sha256, int $size, string $mimeType): selfSet once, by the vault, after the file is written.
Document::isConfidential()
public function isConfidential(): boolDocument::setConfidential()
public function setConfidential(bool $confidential): selfDocument::getContext()
public function getContext(): ?stringDocument::setContext()
public function setContext(?string $context): selfDocument::getExpiresAt()
public function getExpiresAt(): ?\DateTimeImmutableDocument::setExpiresAt()
public function setExpiresAt(?\DateTimeInterface $at): selfDocument::getReadAt()
public function getReadAt(): ?\DateTimeImmutableDocument::markRead()
public function markRead(): selfDocument::isRead()
public function isRead(): boolDocument::getRevokedAt()
public function getRevokedAt(): ?\DateTimeImmutableDocument::revoke()
public function revoke(): selfDocument::isRevoked()
public function isRevoked(): boolDocument::getCreatedAt()
public function getCreatedAt(): \DateTimeImmutableDocument::isExpired()
public function isExpired(?\DateTimeInterface $now = null): boolDocument::isAvailable()
public function isAvailable(): boolStill to be read at all: neither revoked nor expired.
Document::isImage()
public function isImage(): boolBase\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(): ?intRoom::getToken()
public function getToken(): stringRoom::getSubjectKey()
public function getSubjectKey(): stringRoom::getHost()
public function getHost(): ?UserRoom::setHost()
public function setHost(?User $host): selfRoom::getGuest()
public function getGuest(): ?UserRoom::setGuest()
public function setGuest(?User $guest): selfRoom::getOpensAt()
public function getOpensAt(): \DateTimeImmutableRoom::getClosesAt()
public function getClosesAt(): \DateTimeImmutableRoom::reschedule()
public function reschedule(\DateTimeInterface $opensAt, \DateTimeInterface $closesAt): selfRoom::getHostSeenAt()
public function getHostSeenAt(): ?\DateTimeImmutableRoom::getGuestSeenAt()
public function getGuestSeenAt(): ?\DateTimeImmutableRoom::getGuestWaitingSince()
public function getGuestWaitingSince(): ?\DateTimeImmutableRoom::getConnectedAt()
public function getConnectedAt(): ?\DateTimeImmutableRoom::getEndedAt()
public function getEndedAt(): ?\DateTimeImmutableRoom::getCreatedAt()
public function getCreatedAt(): \DateTimeImmutableRoom::getGateway()
public function getGateway(): ?stringRoom::getReference()
public function getReference(): ?stringRoom::openedOn()
public function openedOn(string $gateway, string $reference): selfOpened on a gateway: its name, and the room's reference there.
Room::isOpenedOnGateway()
public function isOpenedOnGateway(): boolRoom::isHost()
public function isHost(?User $user): boolRoom::isGuest()
public function isGuest(?User $user): boolRoom::isParticipant()
public function isParticipant(?User $user): boolRoom::isOpen()
public function isOpen(?\DateTimeInterface $now = null): boolRoom::seen()
public function seen(User $user, ?\DateTimeInterface $now = null): selfSomeone said "I am here" in the last $seconds.
Room::isHostPresent()
public function isHostPresent(int $seconds = 20, ?\DateTimeInterface $now = null): boolRoom::isGuestPresent()
public function isGuestPresent(int $seconds = 20, ?\DateTimeInterface $now = null): boolRoom::isGuestWaiting()
public function isGuestWaiting(int $seconds = 20, ?\DateTimeInterface $now = null): boolThe guest is there and the host is not: the agenda says "in the waiting room".
Room::markConnected()
public function markConnected(): selfRoom::end()
public function end(): selfSignal 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(): ?intSignal::getRoom()
public function getRoom(): RoomSignal::getSender()
public function getSender(): ?UserSignal::getType()
public function getType(): stringSignal::getPayload()
public function getPayload(): stringSignal::getCreatedAt()
public function getCreatedAt(): \DateTimeImmutableBase\Office\Enum
AccessAction enum
enum AccessAction AccessAction.php:5
case VIEW = 'view'case DOWNLOAD = 'download'case UPLOAD = 'upload'case REVOKE = 'revoke'case DENIED = 'denied'Someone asked and was refused.
AccessAction::label()
public function label(): stringAppointmentSource enum
enum AppointmentSource AppointmentSource.php:5
case ONLINE = 'online'case PHONE = 'phone'case STAFF = 'staff'
AppointmentSource::label()
public function label(): stringAppointmentStatus 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(): boolHolds its slot: nobody else may book it.
AppointmentStatus::label()
public function label(): stringBookableBy 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(): stringBookingMode 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(): stringChannel 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(): stringDocumentKind 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(): stringBase\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 $appointmentpublic readonly ?string $cancelTokenpublic readonly array $seriesthe others booked with it
public readonly ?int $hoursBeforepublic 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)$seriesthe 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 $userpublic 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 $documentpublic 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(): stringBookingException::getParameters()
public function getParameters(): arrayCorruptedException 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(): stringShareException::getParameters()
public function getParameters(): arrayBase\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): voidAppointmentRequestType::buildForm()
public function buildForm(FormBuilderInterface $builder, array $options): voidBase\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 = nullpublic ?Member $member = nullpublic ?string $name = nullpublic ?string $email = nullpublic ?string $phone = nullpublic ?string $message = nullpublic ?string $website = nullLeft empty by people; filled by the robots that fill every field.
AppointmentRequest::isRobot()
public function isRobot(): boolBase\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$extraexpressions of the site's own, matched as they are written (case and accents apart)
$patternsa 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 = []): boolWording::plain()
public static function plain(string $text): stringLowered, 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 $startpublic \DateTimeImmutable $endpublic ?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(): stringBase\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(): arrayAreaRepository::findOneActiveBySlug()
public function findOneActiveBySlug(string $slug): ?AreaAreaRepository::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): arrayContactRequestRepository::countOpen()
public function countOpen(): intContactRequestRepository::purgeBefore()
public function purgeBefore(\DateTimeInterface $before): intMemberRepository 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): ?MemberMemberRepository::findOneByUser()
public function findOneByUser(?object $user): ?MemberOfficeRepository class
class OfficeRepository extends ServiceEntityRepository OfficeRepository.php:10
OfficeRepository::__construct()
public function __construct(ManagerRegistry $registry)OfficeRepository::findMain()
public function findMain(): ?OfficeOfficeRepository::findOrdered()
public function findOrdered(): arrayWalkInRepository class
class WalkInRepository extends ServiceEntityRepository WalkInRepository.php:10
WalkInRepository::__construct()
public function __construct(ManagerRegistry $registry)WalkInRepository::findActive()
public function findActive(): arrayBase\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): boolWhether the client had an appointment with that member before (BookableBy::KNOWN).
AppointmentRepository::findOneByCancelToken()
public function findOneByCancelToken(string $token): ?AppointmentAppointmentRepository::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): arrayAppointmentRepository::purgeEndedBefore()
public function purgeEndedBefore(\DateTimeInterface $before): intAppointmentTypeRepository 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): arrayServiceAreaRepository class
class ServiceAreaRepository extends ServiceEntityRepository ServiceAreaRepository.php:10
ServiceAreaRepository::__construct()
public function __construct(ManagerRegistry $registry)ServiceAreaRepository::findActive()
public function findActive(): arrayServiceAreaRepository::findContaining()
public function findContaining(?string $postalCode, ?string $town = null, ?float $latitude = null, ?float $longitude = null): ?ServiceAreaThe 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): intDocumentRepository 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): intDocumentRepository::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): ?RoomRoomRepository::findOneBySubject()
public function findOneBySubject(string $key): ?RoomRoomRepository::findOneByReference()
public function findOneByReference(string $reference, ?string $gateway = null): ?RoomThe 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): intSignalRepository::read()
public function read(string $room, int $after = 0, ?string $except = null, int $limit = 200): arraySignalRepository::purge()
public function purge(string $room): intSignalRepository::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): intSignalRepository::countForRoom()
public function countForRoom(Room $room): intBase\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): InvitationClientInvitations::find()
public function find(string $token): ?InvitationClientInvitations::accept()
public function accept(Invitation $invitation, string $username, string $plainPassword): UserClientInvitations::attach()
public function attach(User $user, string $email): intThe 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)$omnistatewired when glitchr/omnistate is installed
MemberRegistry::isAvailable()
public function isAvailable(): boolMemberRegistry::refresh()
public function refresh(Member $member): ?objectAsk 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): arrayWhat 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).
AudienceResolverInterface::decide()
public function decide(string $attribute, Document $document, UserInterface $user): ?boolCipher 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.
Cipher::__construct()
public function __construct(?string $masterKey = null)Cipher::generateKey()
public static function generateKey(): stringA new master key, base64: what secrets:set SHARE_MASTER_KEY should be given.
Cipher::isConfigured()
public function isConfigured(): boolCipher::encryptStream()
public function encryptStream($in, $out): arrayEncrypts $in to $out.
Cipher::decryptStream()
public function decryptStream($in, $out, string $wrappedKey): stringDecrypts $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): ?stringA short text sealed: "v1:" + base64(nonce + box). Null stays null.
Cipher::decryptText()
public function decryptText(?string $cipher): ?stringCipher::isSealed()
public function isSealed(?string $value): boolDocumentVault 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(): boolDocumentVault::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): DocumentKeep a file for $recipient. The cipher refuses first when there is no key: nothing is written at all.
$filean 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): stringDocumentVault::title()
public function title(Document $document): stringDocumentVault::filename()
public function filename(Document $document): stringDocumentVault::revoke()
public function revoke(Document $document, ?User $by): voidDocumentVault::destroy()
public function destroy(Document $document): voidThe 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): AccessLogShareMailer 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): voidBase\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(): arrayOfficeExtension::getFilters()
public function getFilters(): arrayBase\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(): stringThe name of the gateway new rooms open on.
Gateways::has()
public function has(?string $name): boolGateways::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): ?stringThe factory behind a gateway's name: "direct", "jitsi"... Null when there is no such gateway.
Gateways::isDirect()
public function isDirect(?string $name = null): boolWhether the call goes from browser to browser, its handshake relayed here.
Gateways::turn()
public function turn(?string $name = null): ?TurnCredentialsThe 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(): ?UserRoomSubjectInterface::getVisioGuest()
public function getVisioGuest(): ?UserRoomSubjectInterface::getVisioStartsAt()
public function getVisioStartsAt(): ?\DateTimeImmutableRoomSubjectInterface::getVisioEndsAt()
public function getVisioEndsAt(): ?\DateTimeImmutableRoomSubjectInterface::getVisioKey()
public function getVisioKey(): stringRooms 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): RoomRooms::canEnter()
public function canEnter(Room $room, ?User $user, ?\DateTimeInterface $now = null): boolA participant, while it is open.
Rooms::access()
public function access(Room $room, User $user, ?string $locale = null): AccessHow 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): stringThe 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): voidThe two are talking to each other (the handshake's answer was given).
Rooms::isDirect()
public function isDirect(Room $room): boolWhether the room's call goes from browser to browser, its handshake relayed by the site.
Rooms::meeting()
public function meeting(Room $room): MeetingThe 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): ParticipantSomeone 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): stringThe 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): voidRooms::purge()
public function purge(?\DateTimeInterface $now = null): intClosed 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): SignalSignaling::receive()
public function receive(Room $room, User $user, int $after = 0): arrayWhat 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()).