for the SPA shell. * * The customer portal gets its own per-company title; everything else gets the * instance-wide admin title. An empty or missing setting falls through to the * product name, and an uninstalled instance has no title at all. * * @param mixed $company_id * @return string|null */ function get_page_title($company_id) { if (! InstallationState::isDbCreated()) { return null; } $configured = Route::currentRouteName() === 'customer.dashboard' ? CompanySetting::getSetting('customer_portal_page_title', $company_id) : Setting::getSetting('admin_page_title'); return $configured ?: 'InvoiceShelf - Self Hosted Invoicing Platform'; } /* |-------------------------------------------------------------------------- | Request path matching |-------------------------------------------------------------------------- */ /** * Does the current request path match one of the given patterns? * * @param string|array $path One pattern, or a list of them. * @return bool */ function is_url($path) { return Request::is(...(array) $path); } /** * Blade sugar: emit the marker class when the current path matches. * * @param string|array $path * @param string $active Returned on a match. * @return string */ function set_active($path, $active = 'active') { return is_url($path) ? $active : ''; } /* |-------------------------------------------------------------------------- | Custom fields |-------------------------------------------------------------------------- */ /** * Which custom_field_values column stores an answer of this field type. * * Every value column is typed, so the field's declared type decides where the * answer is written and read. Phone numbers are kept alongside numbers, and an * unrecognised type is treated as free text so an unknown field never loses * its answer. * * @return string */ function getCustomFieldValueKey(string $type) { return match ($type) { 'Number', 'Phone' => 'number_answer', 'Switch' => 'boolean_answer', 'Date' => 'date_answer', 'Time' => 'time_answer', 'DateTime' => 'date_time_answer', // 'Input', 'TextArea', 'Url', 'Dropdown' — and anything unmapped. default => 'string_answer', }; } /** * Every existing slug that could collide with $slug for this model type. * * Prefix-matched in one query so the caller can test the base slug and all of * its numbered variants without going back to the database. The field being * renamed is excluded, otherwise it would collide with itself. * * @param string $type Model type the field is attached to. * @param string $slug Base slug to match as a prefix. * @param int $id Custom field to leave out of the comparison. * @return Collection */ function getRelatedSlugs($type, $slug, $id = 0) { return CustomField::query() ->select('slug') ->where('model_type', $type) ->where('slug', 'like', $slug.'%') ->where('id', '!=', $id) ->get(); } /** * Build the unique storage slug for a custom field. * * The shape is CUSTOM__