xiaowang/kirby/src/Cms/Site.php
2021-11-18 17:44:47 +01:00

699 lines
15 KiB
PHP

<?php
namespace Kirby\Cms;
use Kirby\Exception\InvalidArgumentException;
use Kirby\Exception\LogicException;
use Kirby\Filesystem\Dir;
use Kirby\Panel\Site as Panel;
use Kirby\Toolkit\A;
/**
* The `$site` object is the root element
* for any site with pages. It represents
* the main content folder with its
* `site.txt`.
*
* @package Kirby Cms
* @author Bastian Allgeier <bastian@getkirby.com>
* @link https://getkirby.com
* @copyright Bastian Allgeier GmbH
* @license https://getkirby.com/license
*/
class Site extends ModelWithContent
{
const CLASS_ALIAS = 'site';
use SiteActions;
use HasChildren;
use HasFiles;
use HasMethods;
/**
* The SiteBlueprint object
*
* @var \Kirby\Cms\SiteBlueprint
*/
protected $blueprint;
/**
* The error page object
*
* @var \Kirby\Cms\Page
*/
protected $errorPage;
/**
* The id of the error page, which is
* fetched in the errorPage method
*
* @var string
*/
protected $errorPageId = 'error';
/**
* The home page object
*
* @var \Kirby\Cms\Page
*/
protected $homePage;
/**
* The id of the home page, which is
* fetched in the errorPage method
*
* @var string
*/
protected $homePageId = 'home';
/**
* Cache for the inventory array
*
* @var array
*/
protected $inventory;
/**
* The current page object
*
* @var \Kirby\Cms\Page
*/
protected $page;
/**
* The absolute path to the site directory
*
* @var string
*/
protected $root;
/**
* The page url
*
* @var string
*/
protected $url;
/**
* Modified getter to also return fields
* from the content
*
* @param string $method
* @param array $arguments
* @return mixed
*/
public function __call(string $method, array $arguments = [])
{
// public property access
if (isset($this->$method) === true) {
return $this->$method;
}
// site methods
if ($this->hasMethod($method)) {
return $this->callMethod($method, $arguments);
}
// return site content otherwise
return $this->content()->get($method);
}
/**
* Creates a new Site object
*
* @param array $props
*/
public function __construct(array $props = [])
{
$this->setProperties($props);
}
/**
* Improved `var_dump` output
*
* @return array
*/
public function __debugInfo(): array
{
return array_merge($this->toArray(), [
'content' => $this->content(),
'children' => $this->children(),
'files' => $this->files(),
]);
}
/**
* Makes it possible to convert the site model
* to a string. Mostly useful for debugging.
*
* @return string
*/
public function __toString(): string
{
return $this->url();
}
/**
* Returns the url to the api endpoint
*
* @internal
* @param bool $relative
* @return string
*/
public function apiUrl(bool $relative = false): string
{
if ($relative === true) {
return 'site';
} else {
return $this->kirby()->url('api') . '/site';
}
}
/**
* Returns the blueprint object
*
* @return \Kirby\Cms\SiteBlueprint
*/
public function blueprint()
{
if (is_a($this->blueprint, 'Kirby\Cms\SiteBlueprint') === true) {
return $this->blueprint;
}
return $this->blueprint = SiteBlueprint::factory('site', null, $this);
}
/**
* Builds a breadcrumb collection
*
* @return \Kirby\Cms\Pages
*/
public function breadcrumb()
{
// get all parents and flip the order
$crumb = $this->page()->parents()->flip();
// add the home page
$crumb->prepend($this->homePage()->id(), $this->homePage());
// add the active page
$crumb->append($this->page()->id(), $this->page());
return $crumb;
}
/**
* Prepares the content for the write method
*
* @internal
* @param array $data
* @param string|null $languageCode
* @return array
*/
public function contentFileData(array $data, ?string $languageCode = null): array
{
return A::prepend($data, [
'title' => $data['title'] ?? null,
]);
}
/**
* Filename for the content file
*
* @internal
* @return string
*/
public function contentFileName(): string
{
return 'site';
}
/**
* Returns the error page object
*
* @return \Kirby\Cms\Page|null
*/
public function errorPage()
{
if (is_a($this->errorPage, 'Kirby\Cms\Page') === true) {
return $this->errorPage;
}
if ($error = $this->find($this->errorPageId())) {
return $this->errorPage = $error;
}
return null;
}
/**
* Returns the global error page id
*
* @internal
* @return string
*/
public function errorPageId(): string
{
return $this->errorPageId ?? 'error';
}
/**
* Checks if the site exists on disk
*
* @return bool
*/
public function exists(): bool
{
return is_dir($this->root()) === true;
}
/**
* Returns the home page object
*
* @return \Kirby\Cms\Page|null
*/
public function homePage()
{
if (is_a($this->homePage, 'Kirby\Cms\Page') === true) {
return $this->homePage;
}
if ($home = $this->find($this->homePageId())) {
return $this->homePage = $home;
}
return null;
}
/**
* Returns the global home page id
*
* @internal
* @return string
*/
public function homePageId(): string
{
return $this->homePageId ?? 'home';
}
/**
* Creates an inventory of all files
* and children in the site directory
*
* @internal
* @return array
*/
public function inventory(): array
{
if ($this->inventory !== null) {
return $this->inventory;
}
$kirby = $this->kirby();
return $this->inventory = Dir::inventory(
$this->root(),
$kirby->contentExtension(),
$kirby->contentIgnore(),
$kirby->multilang()
);
}
/**
* Compares the current object with the given site object
*
* @param mixed $site
* @return bool
*/
public function is($site): bool
{
if (is_a($site, 'Kirby\Cms\Site') === false) {
return false;
}
return $this === $site;
}
/**
* Returns the root to the media folder for the site
*
* @internal
* @return string
*/
public function mediaRoot(): string
{
return $this->kirby()->root('media') . '/site';
}
/**
* The site's base url for any files
*
* @internal
* @return string
*/
public function mediaUrl(): string
{
return $this->kirby()->url('media') . '/site';
}
/**
* Gets the last modification date of all pages
* in the content folder.
*
* @param string|null $format
* @param string|null $handler
* @return int|string
*/
public function modified(?string $format = null, ?string $handler = null)
{
return Dir::modified(
$this->root(),
$format,
$handler ?? $this->kirby()->option('date.handler', 'date')
);
}
/**
* Returns the current page if `$path`
* is not specified. Otherwise it will try
* to find a page by the given path.
*
* If no current page is set with the page
* prop, the home page will be returned if
* it can be found. (see `Site::homePage()`)
*
* @param string|null $path omit for current page,
* otherwise e.g. `notes/across-the-ocean`
* @return \Kirby\Cms\Page|null
*/
public function page(?string $path = null)
{
if ($path !== null) {
return $this->find($path);
}
if (is_a($this->page, 'Kirby\Cms\Page') === true) {
return $this->page;
}
try {
return $this->page = $this->homePage();
} catch (LogicException $e) {
return $this->page = null;
}
}
/**
* Alias for `Site::children()`
*
* @return \Kirby\Cms\Pages
*/
public function pages()
{
return $this->children();
}
/**
* Returns the panel info object
*
* @return \Kirby\Panel\Site
*/
public function panel()
{
return new Panel($this);
}
/**
* Returns the permissions object for this site
*
* @return \Kirby\Cms\SitePermissions
*/
public function permissions()
{
return new SitePermissions($this);
}
/**
* Preview Url
*
* @internal
* @return string|null
*/
public function previewUrl(): ?string
{
$preview = $this->blueprint()->preview();
if ($preview === false) {
return null;
}
if ($preview === true) {
$url = $this->url();
} else {
$url = $preview;
}
return $url;
}
/**
* Returns the absolute path to the content directory
*
* @return string
*/
public function root(): string
{
return $this->root = $this->root ?? $this->kirby()->root('content');
}
/**
* Returns the SiteRules class instance
* which is being used in various methods
* to check for valid actions and input.
*
* @return \Kirby\Cms\SiteRules
*/
protected function rules()
{
return new SiteRules();
}
/**
* Search all pages in the site
*
* @param string|null $query
* @param array $params
* @return \Kirby\Cms\Pages
*/
public function search(?string $query = null, $params = [])
{
return $this->index()->search($query, $params);
}
/**
* Sets the Blueprint object
*
* @param array|null $blueprint
* @return $this
*/
protected function setBlueprint(?array $blueprint = null)
{
if ($blueprint !== null) {
$blueprint['model'] = $this;
$this->blueprint = new SiteBlueprint($blueprint);
}
return $this;
}
/**
* Sets the id of the error page, which
* is used in the errorPage method
* to get the default error page if nothing
* else is set.
*
* @param string $id
* @return $this
*/
protected function setErrorPageId(string $id = 'error')
{
$this->errorPageId = $id;
return $this;
}
/**
* Sets the id of the home page, which
* is used in the homePage method
* to get the default home page if nothing
* else is set.
*
* @param string $id
* @return $this
*/
protected function setHomePageId(string $id = 'home')
{
$this->homePageId = $id;
return $this;
}
/**
* Sets the current page object
*
* @internal
* @param \Kirby\Cms\Page|null $page
* @return $this
*/
public function setPage(?Page $page = null)
{
$this->page = $page;
return $this;
}
/**
* Sets the Url
*
* @param string|null $url
* @return $this
*/
protected function setUrl(?string $url = null)
{
$this->url = $url;
return $this;
}
/**
* Converts the most important site
* properties to an array
*
* @return array
*/
public function toArray(): array
{
return [
'children' => $this->children()->keys(),
'content' => $this->content()->toArray(),
'errorPage' => $this->errorPage() ? $this->errorPage()->id(): false,
'files' => $this->files()->keys(),
'homePage' => $this->homePage() ? $this->homePage()->id(): false,
'page' => $this->page() ? $this->page()->id(): false,
'title' => $this->title()->value(),
'url' => $this->url(),
];
}
/**
* Returns the Url
*
* @param string|null $language
* @return string
*/
public function url(?string $language = null): string
{
if ($language !== null || $this->kirby()->multilang() === true) {
return $this->urlForLanguage($language);
}
return $this->url ?? $this->kirby()->url();
}
/**
* Returns the translated url
*
* @internal
* @param string|null $languageCode
* @param array|null $options
* @return string
*/
public function urlForLanguage(?string $languageCode = null, ?array $options = null): string
{
if ($language = $this->kirby()->language($languageCode)) {
return $language->url();
}
return $this->kirby()->url();
}
/**
* Sets the current page by
* id or page object and
* returns the current page
*
* @internal
* @param string|\Kirby\Cms\Page $page
* @param string|null $languageCode
* @return \Kirby\Cms\Page
*/
public function visit($page, ?string $languageCode = null)
{
if ($languageCode !== null) {
$this->kirby()->setCurrentTranslation($languageCode);
$this->kirby()->setCurrentLanguage($languageCode);
}
// convert ids to a Page object
if (is_string($page)) {
$page = $this->find($page);
}
// handle invalid pages
if (is_a($page, 'Kirby\Cms\Page') === false) {
throw new InvalidArgumentException('Invalid page object');
}
// set the current active page
$this->setPage($page);
// return the page
return $page;
}
/**
* Checks if any content of the site has been
* modified after the given unix timestamp
* This is mainly used to auto-update the cache
*
* @param mixed $time
* @return bool
*/
public function wasModifiedAfter($time): bool
{
return Dir::wasModifiedAfter($this->root(), $time);
}
/**
* Deprecated!
*/
/**
* Returns the full path without leading slash
*
* @todo Add `deprecated()` helper warning in 3.7.0
* @todo Remove in 3.8.0
*
* @internal
* @return string
* @codeCoverageIgnore
*/
public function panelPath(): string
{
return $this->panel()->path();
}
/**
* Returns the url to the editing view
* in the panel
*
* @todo Add `deprecated()` helper warning in 3.7.0
* @todo Remove in 3.8.0
*
* @internal
* @param bool $relative
* @return string
* @codeCoverageIgnore
*/
public function panelUrl(bool $relative = false): string
{
return $this->panel()->url($relative);
}
}