Skip to content

Commit

Permalink
React with lock exception and handle accordingly in the panel
Browse files Browse the repository at this point in the history
  • Loading branch information
bastianallgeier committed Dec 5, 2024
1 parent e15c4ab commit b226a7f
Show file tree
Hide file tree
Showing 5 changed files with 158 additions and 19 deletions.
60 changes: 60 additions & 0 deletions panel/src/components/Dialogs/LockDialog.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
<template>
<k-dialog
ref="dialog"
v-bind="$props"
class="k-lock-dialog"
@cancel="$emit('cancel')"
@submit="$emit('submit')"
>
<k-dialog-text :text="$t('form.locked')" />

<dl>
<div>
<dt><k-icon type="user" /></dt>
<dd>{{ lock.user.email }}</dd>
</div>
<div>
<dt><k-icon type="clock" /></dt>
<dd>
{{ $library.dayjs(lock.modified).format("YYYY-MM-DD HH:mm:ss") }}
</dd>
</div>
</dl>
</k-dialog>
</template>

<script>
import Dialog from "@/mixins/dialog.js";
export const props = {
mixins: [Dialog],
props: {
cancelButton: {
default: false
},
lock: Object,
preview: String
}
};
export default {
mixins: [props]
};
</script>

<style>
.k-lock-dialog dl {
margin: var(--spacing-6) 0 var(--spacing-2) 0;
}
.k-lock-dialog dl div {
padding: var(--spacing-1) 0;
line-height: var(--leading-normal);
display: flex;
align-items: center;
gap: 0.75rem;
color: var(--color-gray-500);
}
.k-lock-dialog .k-dialog-buttons {
grid-template-columns: 1fr;
}
</style>
2 changes: 2 additions & 0 deletions panel/src/components/Dialogs/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import FilesDialog from "./FilesDialog.vue";
import FormDialog from "./FormDialog.vue";
import LanguageDialog from "./LanguageDialog.vue";
import LicenseDialog from "./LicenseDialog.vue";
import LockDialog from "./LockDialog.vue";
import LinkDialog from "./LinkDialog.vue";
import ModelsDialog from "./ModelsDialog.vue";
import PageCreateDialog from "./PageCreateDialog.vue";
Expand All @@ -38,6 +39,7 @@ export default {
app.component("k-form-dialog", FormDialog);
app.component("k-license-dialog", LicenseDialog);
app.component("k-link-dialog", LinkDialog);
app.component("k-lock-dialog", LockDialog);
app.component("k-language-dialog", LanguageDialog);
app.component("k-models-dialog", ModelsDialog);
app.component("k-page-create-dialog", PageCreateDialog);
Expand Down
65 changes: 52 additions & 13 deletions panel/src/panel/content.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,14 @@ export default (panel) => {
}

panel.events.emit("content.discard", { api });
} catch (error) {
// handle locked states
if (error.key === "error.lock") {
return this.lockDialog(error.details);
}

// let our regular error handler take over
throw error;
} finally {
this.isProcessing = false;
}
Expand Down Expand Up @@ -104,6 +112,26 @@ export default (panel) => {
return panel.view.props.lock;
},

/**
* Opens the lock dialog to inform the current editor
* about edits from another user
*/
lockDialog(lock) {
this.dialog = panel.dialog;
this.dialog.open({
component: "k-lock-dialog",
props: {
lock: lock
},
on: {
close: () => {
this.dialog = null;
panel.view.reload();
}
}
});
},

/**
* Merge new content changes with the
* original values and update the view props
Expand Down Expand Up @@ -135,12 +163,6 @@ export default (panel) => {
return;
}

// In the current view, we can use the existing
// lock state to determine if changes can be published
if (this.isCurrent(api) === true && this.isLocked(api) === true) {
throw new Error("Cannot publish locked changes");
}

this.isProcessing = true;

// Send updated values to API
Expand All @@ -157,6 +179,11 @@ export default (panel) => {

panel.events.emit("content.publish", { values, api });
} catch (error) {
// handle locked states
if (error.key === "error.lock") {
return this.lockDialog(error.details);
}

this.retry("publish", error, panel.view.props.content, api);
} finally {
this.isProcessing = false;
Expand Down Expand Up @@ -212,9 +239,9 @@ export default (panel) => {
async save(values, api) {
api ??= panel.view.props.api;

if (this.isCurrent(api) === true && this.isLocked(api) === true) {
throw new Error("Cannot save locked changes");
}
// if (this.isCurrent(api) === true && this.isLocked(api) === true) {
// throw new Error("Cannot save locked changes");
// }

this.isProcessing = true;

Expand All @@ -241,11 +268,23 @@ export default (panel) => {

panel.events.emit("content.save", { api, values });
} catch (error) {
// silent aborted requests, but throw all other errors
if (error.name !== "AbortError") {
this.isProcessing = false;
this.retry("save", error, panel.view.props.content, api);
// handle aborted requests silently
if (error.name === "AbortError") {
return;
}

// processing must not be interrupted for aborted
// requests because the follow-up request is already
// in progress and setting the state to false here
// would be wrong
this.isProcessing = false;

// handle locked states
if (error.key === "error.lock") {
return this.lockDialog(error.details);
}

this.retry("save", error, panel.view.props.content, api);
}
},

Expand Down
32 changes: 32 additions & 0 deletions src/Content/LockException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php

namespace Kirby\Content;

use Kirby\Exception\LogicException;

/**
* @package Kirby Content
* @author Bastian Allgeier <[email protected]>
* @link https://getkirby.com
* @copyright Bastian Allgeier
* @license https://getkirby.com/license
*/
class LockException extends LogicException
{
protected static string $defaultKey = 'lock';
protected static string $defaultFallback = 'The version is locked';
protected static int $defaultHttpCode = 423;

public function __construct(
Lock $lock,
string|null $key = null,
string|null $message = null,
) {
parent::__construct(
message: $message,
key: $key,
details: $lock->toArray()
);
}

}
18 changes: 12 additions & 6 deletions src/Content/VersionRules.php
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,8 @@ public static function delete(
Language $language
): void {
if ($version->isLocked('*') === true) {
throw new LogicException(
throw new LockException(
lock: $version->lock('*'),
message: 'The version is locked and cannot be deleted'
);
}
Expand All @@ -85,14 +86,16 @@ public static function move(

// check if the source version is locked in any language
if ($fromVersion->isLocked('*') === true) {
throw new LogicException(
throw new LockException(
lock: $fromVersion->lock('*'),
message: 'The source version is locked and cannot be moved'
);
}

// check if the target version is locked in any language
if ($toVersion->isLocked('*') === true) {
throw new LogicException(
throw new LockException(
lock: $toVersion->lock('*'),
message: 'The target version is locked and cannot be overwritten'
);
}
Expand All @@ -114,7 +117,8 @@ public static function publish(

// check if the version is locked in any language
if ($version->isLocked('*') === true) {
throw new LogicException(
throw new LockException(
lock: $version->lock('*'),
message: 'The version is locked and cannot be published'
);
}
Expand All @@ -137,7 +141,8 @@ public static function replace(

// check if the version is locked in any language
if ($version->isLocked('*') === true) {
throw new LogicException(
throw new LockException(
lock: $version->lock('*'),
message: 'The version is locked and cannot be replaced'
);
}
Expand All @@ -158,7 +163,8 @@ public static function update(
static::ensure($version, $language);

if ($version->isLocked('*') === true) {
throw new LogicException(
throw new LockException(
lock: $version->lock('*'),
message: 'The version is locked and cannot be updated'
);
}
Expand Down

0 comments on commit b226a7f

Please sign in to comment.