256 lines
4.9 KiB
PHP
256 lines
4.9 KiB
PHP
<?php
|
|
|
|
namespace Kirby\Cms;
|
|
|
|
use Kirby\Exception\DuplicateException;
|
|
use Kirby\Exception\LogicException;
|
|
use Kirby\Exception\PermissionException;
|
|
|
|
/**
|
|
* Takes care of content lock and unlock information
|
|
*
|
|
* @package Kirby Cms
|
|
* @author Nico Hoffmann <nico@getkirby.com>
|
|
* @link https://getkirby.com
|
|
* @copyright Bastian Allgeier
|
|
* @license https://getkirby.com/license
|
|
*/
|
|
class ContentLock
|
|
{
|
|
/**
|
|
* Lock data
|
|
*
|
|
* @var array
|
|
*/
|
|
protected $data;
|
|
|
|
/**
|
|
* The model to manage locking/unlocking for
|
|
*
|
|
* @var ModelWithContent
|
|
*/
|
|
protected $model;
|
|
|
|
/**
|
|
* @param \Kirby\Cms\ModelWithContent $model
|
|
*/
|
|
public function __construct(ModelWithContent $model)
|
|
{
|
|
$this->model = $model;
|
|
$this->data = $this->kirby()->locks()->get($model);
|
|
}
|
|
|
|
/**
|
|
* Clears the lock unconditionally
|
|
*
|
|
* @return bool
|
|
*/
|
|
protected function clearLock(): bool
|
|
{
|
|
// if no lock exists, skip
|
|
if (isset($this->data['lock']) === false) {
|
|
return true;
|
|
}
|
|
|
|
// remove lock
|
|
unset($this->data['lock']);
|
|
|
|
return $this->kirby()->locks()->set($this->model, $this->data);
|
|
}
|
|
|
|
/**
|
|
* Sets lock with the current user
|
|
*
|
|
* @return bool
|
|
* @throws \Kirby\Exception\DuplicateException
|
|
*/
|
|
public function create(): bool
|
|
{
|
|
// check if model is already locked by another user
|
|
if (
|
|
isset($this->data['lock']) === true &&
|
|
$this->data['lock']['user'] !== $this->user()->id()
|
|
) {
|
|
$id = ContentLocks::id($this->model);
|
|
throw new DuplicateException($id . ' is already locked');
|
|
}
|
|
|
|
$this->data['lock'] = [
|
|
'user' => $this->user()->id(),
|
|
'time' => time()
|
|
];
|
|
|
|
return $this->kirby()->locks()->set($this->model, $this->data);
|
|
}
|
|
|
|
/**
|
|
* Returns either `false` or array with `user`, `email`,
|
|
* `time` and `unlockable` keys
|
|
*
|
|
* @return array|bool
|
|
*/
|
|
public function get()
|
|
{
|
|
$data = $this->data['lock'] ?? [];
|
|
|
|
if (empty($data) === false && $data['user'] !== $this->user()->id()) {
|
|
if ($user = $this->kirby()->user($data['user'])) {
|
|
$time = (int)($data['time']);
|
|
|
|
return [
|
|
'user' => $user->id(),
|
|
'email' => $user->email(),
|
|
'time' => $time,
|
|
'unlockable' => ($time + 60) <= time()
|
|
];
|
|
}
|
|
|
|
// clear lock if user not found
|
|
$this->clearLock();
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Returns if the model is locked by another user
|
|
*
|
|
* @return bool
|
|
*/
|
|
public function isLocked(): bool
|
|
{
|
|
$lock = $this->get();
|
|
|
|
if ($lock !== false && $lock['user'] !== $this->user()->id()) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Returns if the current user's lock has been removed by another user
|
|
*
|
|
* @return bool
|
|
*/
|
|
public function isUnlocked(): bool
|
|
{
|
|
$data = $this->data['unlock'] ?? [];
|
|
|
|
return in_array($this->user()->id(), $data) === true;
|
|
}
|
|
|
|
/**
|
|
* Returns the app instance
|
|
*
|
|
* @return \Kirby\Cms\App
|
|
*/
|
|
protected function kirby(): App
|
|
{
|
|
return $this->model->kirby();
|
|
}
|
|
|
|
/**
|
|
* Removes lock of current user
|
|
*
|
|
* @return bool
|
|
* @throws \Kirby\Exception\LogicException
|
|
*/
|
|
public function remove(): bool
|
|
{
|
|
// if no lock exists, skip
|
|
if (isset($this->data['lock']) === false) {
|
|
return true;
|
|
}
|
|
|
|
// check if lock was set by another user
|
|
if ($this->data['lock']['user'] !== $this->user()->id()) {
|
|
throw new LogicException([
|
|
'fallback' => 'The content lock can only be removed by the user who created it. Use unlock instead.',
|
|
'httpCode' => 409
|
|
]);
|
|
}
|
|
|
|
return $this->clearLock();
|
|
}
|
|
|
|
/**
|
|
* Removes unlock information for current user
|
|
*
|
|
* @return bool
|
|
*/
|
|
public function resolve(): bool
|
|
{
|
|
// if no unlocks exist, skip
|
|
if (isset($this->data['unlock']) === false) {
|
|
return true;
|
|
}
|
|
|
|
// remove user from unlock array
|
|
$this->data['unlock'] = array_diff(
|
|
$this->data['unlock'],
|
|
[$this->user()->id()]
|
|
);
|
|
|
|
return $this->kirby()->locks()->set($this->model, $this->data);
|
|
}
|
|
|
|
/**
|
|
* Returns the state for the
|
|
* form buttons in the frontend
|
|
*/
|
|
public function state(): ?string
|
|
{
|
|
return match (true) {
|
|
$this->isUnlocked() => 'unlock',
|
|
$this->isLocked() => 'lock',
|
|
default => null
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Returns a usable lock array
|
|
* for the frontend
|
|
*
|
|
* @return array
|
|
*/
|
|
public function toArray(): array
|
|
{
|
|
return [
|
|
'state' => $this->state(),
|
|
'data' => $this->get()
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Removes current lock and adds lock user to unlock data
|
|
*
|
|
* @return bool
|
|
*/
|
|
public function unlock(): bool
|
|
{
|
|
// if no lock exists, skip
|
|
if (isset($this->data['lock']) === false) {
|
|
return true;
|
|
}
|
|
|
|
// add lock user to unlocked data
|
|
$this->data['unlock'] ??= [];
|
|
$this->data['unlock'][] = $this->data['lock']['user'];
|
|
|
|
return $this->clearLock();
|
|
}
|
|
|
|
/**
|
|
* Returns currently authenticated user;
|
|
* throws exception if none is authenticated
|
|
*
|
|
* @return \Kirby\Cms\User
|
|
* @throws \Kirby\Exception\PermissionException
|
|
*/
|
|
protected function user(): User
|
|
{
|
|
return $this->kirby()->user() ??
|
|
throw new PermissionException('No user authenticated.');
|
|
}
|
|
}
|