permissions

This commit is contained in:
2023-10-17 22:57:25 +03:00
parent 4b9e9cb4f6
commit 632684f514
29 changed files with 370 additions and 223 deletions
+2
View File
@@ -23,9 +23,11 @@
export let data;
// export let layout;
export let channel;
export let readableSchemas;
setContext("channel", channel);
setContext("readableSchemas", channel.schemas.filter((s) => readableSchemas.includes(s.name)));
setContext("user", user);
</script>
+4 -3
View File
@@ -5,11 +5,12 @@
export let schema;
const channel = getContext("channel");
const readableSchemas = getContext("readableSchemas");
const user = getContext("user");
let contentIsOpen = false;
const fileSchemas = channel.schemas.filter((sc) => sc.type === "files");
const otherSchemas = channel.schemas.filter((sc) => !sc.isEntry && sc.type === "collection");
const fileSchemas = readableSchemas.filter((sc) => sc.type === "files");
const otherSchemas = readableSchemas.filter((sc) => !sc.isEntry && sc.type === "collection");
</script>
@@ -57,7 +58,7 @@
aria-labelledby="panelsStayOpen-headingMain">
<div class="accordion-body">
<NavbarMenu
schemas={ channel.schemas.filter((sc) => sc.isEntry)}
schemas={ readableSchemas.filter((sc) => sc.isEntry)}
schema={schema}
/>
</div>
+45 -40
View File
@@ -23,6 +23,7 @@
export let inModal;
export let modalUrl;
export let selected = [];
export let isWritable = false;
function selectRecord(e, record) {
@@ -62,68 +63,72 @@
<h3 class="header-normal mb-5 ">
{schema.label}
</h3>
{#if selected.length > 0 && !inModal}
{#if selected.length > 0 && !inModal && isWritable}
<ActionsOnSelected {schema} {selected} {inModal} {filter}/>
{:else}
<Tools
bind:schema
bind:records
{systemFields}
{sort}
{operators}
{filter}
{inModal}
{modalUrl}
on:refresh={refresh}
bind:schema
bind:records
{systemFields}
{sort}
{operators}
{filter}
{inModal}
{modalUrl}
{isWritable}
on:refresh={refresh}
/>
{/if}
{#if schema.type === "collection"}
<Table
{records}
{graph}
{schema}
{sort}
{systemFields}
{inModal}
{users}
bind:selected
{records}
{graph}
{schema}
{sort}
{systemFields}
{inModal}
{users}
{isWritable}
bind:selected
/>
{:else}
<div class="row" style="max-width:1000px">
{#each records as record (record.id)}
<div class="col-6 col-md-4">
<div
class="file-wrapper rounded p-2 mb-4 bg-light"
class:selected={selected.includes(record)}
class="file-wrapper rounded p-2 mb-4 bg-light"
class:selected={selected.includes(record)}
>
<div class="form-check">
<input
on:change={(e) => selectRecord(e, record)}
class="form-check-input "
type="checkbox"
checked={selected.find(
{#if isWritable}
<div class="form-check">
<input
on:change={(e) => selectRecord(e, record)}
class="form-check-input "
type="checkbox"
checked={selected.find(
(r) => r.id === record.id
)}
value={record}
/>
</div>
value={record}
/>
</div>
{/if}
<div class="d-flex justify-content-center">
<Preview {record} size="medium"/>
</div>
<a
href="{channel.lucentUrl}/records/{record.id}"
title={record._file.path}
class="d-block text-center overflow-hidden text-nowrap my-2 "
style="
href="{channel.lucentUrl}/records/{record.id}"
title={record._file.path}
class="d-block text-center overflow-hidden text-nowrap my-2 "
style="
text-overflow: ellipsis;
font-size: 13px;
color: #333;
">{record._file.path}</a
>
<span
class="lx-small-text text-muted d-block text-center"
class="lx-small-text text-muted d-block text-center"
>{record._file.mime}</span
>
</div>
@@ -134,12 +139,12 @@
</div>
<Pagination
{limit}
{skip}
{total}
on:refresh={refresh}
{inModal}
{modalUrl}
{limit}
{skip}
{total}
on:refresh={refresh}
{inModal}
{modalUrl}
/>
</div>
+23 -18
View File
@@ -14,6 +14,7 @@
export let systemFields;
export let sort;
export let inModal;
export let isWritable;
export let selected = [];
function toggleAll(e) {
@@ -44,16 +45,18 @@
<table class="">
<thead class="table-light">
<tr>
<th>
<input
on:change|preventDefault={toggleAll}
indeterminate={selected.length > 0 &&
{#if isWritable}
<th>
<input
on:change|preventDefault={toggleAll}
indeterminate={selected.length > 0 &&
selected.length < records.length}
checked={selected.length == records.length}
class="form-check-input"
type="checkbox"
/>
</th>
checked={selected.length == records.length}
class="form-check-input"
type="checkbox"
/>
</th>
{/if}
{#each visibleColumns as field}
<th
@@ -79,18 +82,20 @@
class="title-td-contents d-inline-flex justify-content-between w-100 align-items-center"
>
<div class="d-flex align-items-center ">
<div class="form-check">
<input
on:change={(e) =>
{#if isWritable}
<div class="form-check">
<input
on:change={(e) =>
selectRecord(e, record)}
class="form-check-input "
type="checkbox"
checked={selected.find(
class="form-check-input "
type="checkbox"
checked={selected.find(
(r) => r.id === record.id
)}
value={record}
/>
</div>
value={record}
/>
</div>
{/if}
<a
+12 -9
View File
@@ -15,6 +15,7 @@
export let filter;
export let inModal;
export let modalUrl;
export let isWritable;
export let records;
export let systemFields = [];
// export let visibleFields = [];
@@ -83,7 +84,7 @@
<div class="d-flex align-items-center ">
{#if schema.type === "collection"}
{#if !inModal}
{#if !inModal && isWritable}
<a
href="{channel.lucentUrl}/records/new?schema={schema.name}"
class="btn btn-sm btn-primary"
@@ -109,14 +110,16 @@
<ul class="dropdown-menu">
{#if filter["status_in"] === "trashed"}
<li>
<a
class="dropdown-item"
href="{channel.lucentUrl}/content/{schema.name}/emptyTrash"
>
Empty trash
</a>
</li>
{#if isWritable}
<li>
<a
class="dropdown-item"
href="{channel.lucentUrl}/content/{schema.name}/emptyTrash"
>
Empty trash
</a>
</li>
{/if}
{:else}
<li>
@@ -7,11 +7,26 @@
export let member;
export let roles;
function update(e, newRole) {
function removeFrom(e, aRole) {
e.preventDefault();
let newRoles = member.roles.filter((r) => r !== aRole);
dispatch("update", {
user: member.id,
role: newRole,
roles: newRoles,
});
}
function addTo(e, aRole) {
e.preventDefault();
let newRoles = [...member.roles, aRole];
console.log(member.roles)
console.log(aRole)
console.log(newRoles)
dispatch("update", {
user: member.id,
roles: newRoles,
});
}
@@ -19,11 +34,11 @@
</script>
<div
transition:fly={{ duration: 200 }}
class="d-flex justify-content-between align-items-center mb-3 "
transition:fly={{ duration: 200 }}
class="d-flex justify-content-between align-items-center mb-3 "
>
<div class="d-flex align-items-center status-{member.role}">
<Avatar name={member.name ?? "" } side="32"/>
<div class="d-flex align-items-center status-{member.roles.includes('removed') ? 'removed' : 'active'}">
<Avatar name={member.name ?? "" } side={32}/>
<div class="ms-3 ">
<div>
<span class="fs-5">
@@ -36,21 +51,37 @@
<div>
<div class="dropdown dropdown-center">
<button
class=" dropdown-toggle btn btn-light"
type="button"
data-bs-toggle="dropdown"
aria-expanded="false"
class=" dropdown-toggle btn btn-light"
type="button"
data-bs-toggle="dropdown"
aria-expanded="false"
>
{member.role}
Roles
</button>
<div class="dropdown-menu">
<h6 class="dropdown-header">Remove role</h6>
{#each roles as role}
{#if member.role !== role}
{#if member.roles.includes(role)}
<button
class="dropdown-item"
on:click={(e) => update(e,role)}
class="dropdown-item text-capitalize"
on:click={(e) => removeFrom(e,role)}
>
Convert to {role}
{role}
</button>
{/if}
{/each}
<div>
<hr class="dropdown-divider">
</div>
<h6 class="dropdown-header">Add role</h6>
{#each roles as role}
{#if !member.roles.includes(role)}
<button
class="dropdown-item text-capitalize"
on:click={(e) => addTo(e,role)}
>
{role}
</button>
{/if}
{/each}
+4 -5
View File
@@ -9,7 +9,6 @@
const channel = getContext("channel");
export let users;
export let roles;
let name;
let email;
let role;
@@ -28,7 +27,7 @@
.post(channel.lucentUrl + "/members/invite", {
name: newName,
email: newEmail,
role: newRole,
roles: [newRole],
})
.then((response) => {
successAlert.show("User was invited");
@@ -49,7 +48,7 @@
axios
.post(channel.lucentUrl + "/members/update", {
id: e.detail.user,
role: e.detail.role,
roles: e.detail.roles,
})
.then((response) => {
successAlert.show("Users updated");
@@ -96,7 +95,7 @@
</div>
<div class="me-3">
{#each roles.filter((r) => r !== "removed") as arole}
{#each channel.roles.filter((r) => r !== "removed") as arole}
<Radio
bind:group={role}
value={arole}
@@ -117,7 +116,7 @@
{#each users as user}
<MemberSettingsCard
member={user}
roles={roles}
roles={channel.roles}
on:update={update}
on:reinvite={(e) => invite(e.detail.email, e.detail.role)}
/>
+2 -1
View File
@@ -21,6 +21,7 @@
};
export let recordHistory;
export let isCreateMode;
export let isWritable = false;
export let users;
let originalContent;
let activeContentTab = "";
@@ -155,7 +156,7 @@
<Manager managerRecords={recordHistory} {graph}/>
<EditHeader {schema} {record} {isCreateMode} {graph} bind:activeContentTab/>
{#if !["_graph", "_info"].includes(activeContentTab)}
{#if !["_graph", "_info"].includes(activeContentTab) && isWritable}
<div class="shadow-lg "
style="position:fixed;bottom:0;left:0px;width:100%;background: rgb(206, 223, 210);z-index:1050"
>
+2 -1
View File
@@ -1,9 +1,10 @@
<script>
export let title;
</script>
<div class="wrapper-normal ">
<div class="header-normal">
Record Not Found
{title}
</div>
</div>
+21 -1
View File
@@ -2,13 +2,18 @@
namespace Lucent\Account;
use Lucent\Channel\ChannelService;
use Lucent\Primitive\Collection;
use Lucent\Schema\SchemaService;
readonly class AccountService
{
public function __construct(
private UserRepo $userRepo,
private AuthService $authService,
private ChannelService $channelService,
private SchemaService $schemaService,
private UserRepo $userRepo,
)
{
@@ -37,5 +42,20 @@ readonly class AccountService
return $this->all()->map(fn($user) => $user->safe());
}
/**
* @return array<string>
*/
public function currentReadableSchemas(): array
{
$roles = $this->authService->currentUserRoles();
return $this->channelService->schemasReadableByRoles($roles);
}
public function currentWritableSchemas(): array
{
$roles = $this->authService->currentUserRoles();
return $this->channelService->schemasWritableByRoles($roles);
}
}
+27 -14
View File
@@ -25,14 +25,19 @@ readonly class AuthService
public function currentUserId(): ?string
{
if(app()->runningInConsole()){
if (app()->runningInConsole()) {
return config("lucent.systemUserId");
}else{
} else {
return $this->session->get("user.id");
}
}
public function currentUserRoles(): array
{
return $this->session->get("user.roles") ?? [];
}
public function isLoggedIn(): bool
{
return !empty($this->currentUserId());
@@ -50,7 +55,7 @@ readonly class AuthService
throw new LucentException("You account was not found");
}
if ($user->get()->role === Role::REMOVED) {
if ($user->get()->isRemoved()) {
throw new LucentException("Your account is not active");
}
@@ -66,18 +71,17 @@ readonly class AuthService
$newUser->updatedAt = Carbon::now()->toJson();
$newUser->mailToken = null;
$this->userRepo->update($newUser);
$this->session->put(["user" => $user->get()->safe()]);
}
public function create(string $name, string $email, string $role): User
public function create(string $name, string $email, array $roles): User
{
$user = new User(
id: (string)Str::uuid(),
name: new Name($name),
email: new Email($email),
role: Role::from($role),
roles: $this->validateRoles($roles),
createdAt: Carbon::now()->toJson(),
updatedAt: Carbon::now()->toJson(),
loggedInAt: Carbon::now()->toJson(),
@@ -101,7 +105,7 @@ readonly class AuthService
throw new LucentException("User not found");
}
if ($user->get()->role === Role::REMOVED) {
if ($user->get()->isRemoved()) {
throw new LucentException("Cannot reset email if the user is not active");
}
@@ -121,7 +125,7 @@ readonly class AuthService
/**
* @throws LucentException
*/
public function changeRole(string $userId, string $newRole): void
public function changeRoles(string $userId, array $roles): void
{
$user = $this->userRepo->findById($userId);
@@ -130,7 +134,7 @@ readonly class AuthService
}
$newUser = $user->get();
$newUser->role = Role::from($newRole);
$newUser->roles = $this->validateRoles($roles);
$newUser->updatedAt = Carbon::now()->toJson();
$this->userRepo->update($newUser);
}
@@ -138,7 +142,7 @@ readonly class AuthService
/**
* @throws LucentException
*/
public function updateName( string $name): void
public function updateName(string $name): void
{
$name = (new Name($name));
$this->userRepo->updateName($this->currentUserId(), $name);
@@ -153,7 +157,7 @@ readonly class AuthService
{
$email = (new Email($email));
$user = $this->userRepo->findByEmail($email);
if($user->isDefined()){
if ($user->isDefined()) {
throw new LucentException("Email already assigned to user");
}
@@ -169,10 +173,10 @@ readonly class AuthService
public function invite(
string $name,
string $email,
string $role
array $roles
): User
{
$user = $this->create($name, $email, $role);
$user = $this->create($name, $email, $roles);
$this->sendLoginEmail($user->email);
return $user;
}
@@ -185,10 +189,19 @@ readonly class AuthService
string $email
): User
{
$user = $this->invite($name, $email, "admin");
$user = $this->invite($name, $email, ["admin"]);
$this->sendLoginEmail($user->email);
return $user;
}
public function validateRoles(array $roles): array
{
return collect($roles)
->filter(fn(string $role) => in_array($role, $this->channelService->channel->roles))
->unique()
->values()
->toArray();
}
}
-25
View File
@@ -1,25 +0,0 @@
<?php
namespace Lucent\Account;
enum Role: string
{
case ADMIN = 'admin';
case EDITOR = 'editor';
case READER = 'reader';
case REMOVED = 'removed';
function hasAccess(string $roleName): bool
{
$trialRole = Role::from($roleName);
$access = match ($trialRole) {
Role::ADMIN => [Role::ADMIN, Role::DEVELOPER, Role::EDITOR, Role::READER],
Role::EDITOR => [Role::EDITOR, Role::READER],
Role::READER => [Role::READER],
Role::REMOVED => [],
};
return in_array($trialRole, $access);
}
}
+9 -15
View File
@@ -6,11 +6,14 @@ namespace Lucent\Account;
class User
{
/**
* @param array<string> $roles
*/
function __construct(
public string $id,
public Name $name,
public Email $email,
public Role $role,
public array $roles,
public string $createdAt,
public string $updatedAt,
public ?string $loggedInAt = null,
@@ -20,24 +23,15 @@ class User
{
}
public static function fromArray(array $data): User
{
return new User(
id: $data["id"],
name: new Name($data["name"] ?? ""),
email: new Email($data["email"]),
role: Role::tryFrom($data["role"]),
createdAt: $data["createdAt"],
updatedAt: $data["updatedAt"],
loggedInAt: $data["loggedInAt"] ?? null,
mailToken: $data["mailToken"] ?? null,
);
}
public function isRemoved()
{
return in_array("removed", $this->roles);
}
public function safe(): array
{
$userData = collect(toArray($this));
return $userData->only(["id", "name", "email", "role", "status"])->toArray();
return $userData->only(["id", "name", "email", "roles", "status"])->toArray();
}
}
+1 -22
View File
@@ -9,31 +9,10 @@ readonly class UserProfile
public string $id,
public string $name,
public string $email,
public Role $role,
public array $roles,
)
{
}
public static function fromUser(User $user): UserProfile
{
return new UserProfile(
$user->id,
$user->name,
$user->email,
$user->role,
);
}
public static function fromArray(array $data): ?UserProfile
{
if (empty($data)) {
return null;
}
return new UserProfile(
id: data_get($data, "id"),
name: data_get($data, "name"),
email: data_get($data, "email"),
role: data_get($data, "role"),
);
}
}
+19 -3
View File
@@ -22,7 +22,7 @@ class UserRepo
{
$usersData = DB::table("users")->get();
$users = array_map(fn($userData) => User::fromArray((array)$userData), $usersData->toArray());
$users = array_map(fn($userData) => $this->fromArray((array)$userData), $usersData->toArray());
return new Collection($users);
}
@@ -30,12 +30,14 @@ class UserRepo
public static function insert(User $user): void
{
$userData = toArray($user);
$userData["roles"] = json_encode($userData["roles"]);
DB::table("users")->insert($userData);
}
public function update(User $user): void
{
$userData = toArray($user);
$userData["roles"] = json_encode($userData["roles"]);
DB::table("users")->where("id", $user->id)->update($userData);
}
@@ -66,7 +68,7 @@ class UserRepo
return none();
}
return some(User::fromArray((array)$user));
return some($this->fromArray((array)$user));
}
/**
@@ -80,7 +82,7 @@ class UserRepo
return none();
}
return some(User::fromArray((array)$user));
return some($this->fromArray((array)$user));
}
@@ -93,4 +95,18 @@ class UserRepo
{
DB::table("users")->where("id", $userId)->update(["email" => $email->value()]);
}
public function fromArray(array $data): User
{
return new User(
id: $data["id"],
name: new Name($data["name"] ?? ""),
email: new Email($data["email"]),
roles: json_decode($data["roles"] ?? [],true),
createdAt: $data["createdAt"],
updatedAt: $data["updatedAt"],
loggedInAt: $data["loggedInAt"] ?? null,
mailToken: $data["mailToken"] ?? null,
);
}
}
+1
View File
@@ -21,6 +21,7 @@ final class Channel
public string $generateCommand,
public Collection $schemas,
public array $imageFilters,
public array $roles,
)
{
$this->lucentUrl = $url . "/lucent";
+30 -7
View File
@@ -22,24 +22,23 @@ final class ChannelService
public static function fromConfig(): ChannelService
{
$schemasArray = [];
if(file_exists(schemas_path())){
if (file_exists(schemas_path())) {
$schemasJson = file_get_contents(schemas_path());
$schemasArray = json_decode($schemasJson, true);
}
$schemaService = new SchemaService();
$schemasCollection = (new Collection($schemasArray))->map([$schemaService, 'fromArray']);
$schemasCollection = (new Collection($schemasArray["schemas"] ?? []))->map([$schemaService, 'fromArray']);
$channel = new Channel(
name: config("lucent.name") ?? "",
url: rtrim( config("lucent.url") ?? "", "/"),
previewTarget: rtrim( config("lucent.previewTarget") ?? "", "/"),
url: rtrim(config("lucent.url") ?? "", "/"),
previewTarget: rtrim(config("lucent.previewTarget") ?? "", "/"),
generateCommand: config("lucent.generateCommand") ?? "",
schemas: $schemasCollection,
imageFilters: config("lucent.imageFilters") ?? [],
roles: $schemasArray["roles"] ?? []
);
$channelService = new ChannelService($schemaService);
$channelService->channel = $channel;
return $channelService;
@@ -58,4 +57,28 @@ final class ChannelService
return some($schema);
}
/**
* @param array<string> $roles
* @return array<string>
*/
public function schemasReadableByRoles(array $roles): array
{
$schemasAllRead = $this->channel->schemas->filter(fn(Schema $schema) => empty($schema->read))->values()->pluck("name");
$schemasCanRead = $this->channel->schemas->filter(fn(Schema $schema) => count(array_intersect($schema->read, $roles)) > 0)->values()->pluck("name");
$schemasCanWrite = $this->channel->schemas->filter(fn(Schema $schema) => count(array_intersect($schema->write, $roles)) > 0)->values()->pluck("name");
return $schemasAllRead->merge($schemasCanRead)->merge($schemasCanWrite)->unique()->values()->toArray();
}
/**
* @param array<string> $roles
* @return array<string>
*/
public function schemasWritableByRoles(array $roles): array
{
$schemasAllRead = $this->channel->schemas->filter(fn(Schema $schema) => empty($schema->write))->values()->pluck("name");
$schemasCanWrite = $this->channel->schemas->filter(fn(Schema $schema) => count(array_intersect($schema->write, $roles)) > 0)->values()->pluck("name");
return $schemasAllRead->merge($schemasCanWrite)->unique()->values()->toArray();
}
}
+24 -4
View File
@@ -4,6 +4,9 @@ namespace Lucent\Commands;
use DirectoryIterator;
use Illuminate\Console\Command;
use Lucent\Schema\Schema;
use Lucent\Schema\SchemaService;
use Lucent\Schema\Type;
class CompileSchemas extends Command
{
@@ -13,7 +16,9 @@ class CompileSchemas extends Command
protected $description = 'Compiles schemas';
public function __construct()
public function __construct(
public SchemaService $schemaService
)
{
parent::__construct();
}
@@ -39,13 +44,28 @@ class CompileSchemas extends Command
}
$schemas = collect($schemas)->sortBy("label")->values()->toArray();
$schemas = collect($schemas)->sortBy("label")->values();
$roles = $schemas
->map([$this->schemaService, 'fromArray'])
->whereIn("type", [Type::COLLECTION, Type::FILES])
->reduce(fn($carry, Schema $schema) => array_merge(
$carry,
$schema->read,
$schema->write,
config("lucent.canInvite"),
config("lucent.canBuild"),
), []);
if(!file_exists(storage_path("lucent"))){
$json = [
"schemas" => $schemas->toArray(),
"roles" => collect($roles)->push("admin")->push("removed")->unique()->values()->toArray()
];
if (!file_exists(storage_path("lucent"))) {
mkdir(storage_path("lucent"));
}
file_put_contents(schemas_path(), json_encode($schemas));
file_put_contents(schemas_path(), json_encode($json));
$this->info("Lucent Schemas were updated");
}
+3 -1
View File
@@ -7,5 +7,7 @@ return [
"url" => env("LUCENT_URL", env('APP_URL')),
"previewTarget" => env("LUCENT_PREVIEW_TARGET", "previewTarget"),
"generateCommand" => env("LUCENT_GENERATE_COMMAND", "generate:static"),
"imageFilters" => []
"imageFilters" => [],
"canInvite" => ["admin"],
"canBuild" => ["admin"],
];
@@ -16,7 +16,7 @@ return new class extends Migration {
$table->uuid("id")->primary();
$table->string('name')->nullable();
$table->string('email')->unique();
$table->string('role');
$table->jsonb('roles');
$table->string('createdAt');
$table->string('updatedAt');
$table->string('loggedInAt');
+3 -5
View File
@@ -7,8 +7,6 @@ use Illuminate\Contracts\View\View;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Lucent\Account\AccountService;
use Lucent\Account\UserRepo;
use Lucent\Channel\ChannelService;
use Lucent\Query\Query;
use Lucent\Svelte\Svelte;
use function Lucent\Response\ok;
@@ -17,7 +15,7 @@ class HomeController extends Controller
{
public function __construct(
private readonly Svelte $svelte,
private readonly AccountService $accountService,
private readonly AccountService $accountService,
private readonly Query $query,
)
{
@@ -36,10 +34,12 @@ class HomeController extends Controller
public function records(Request $request): Response
{
$urlParams = $request->all();
$users = $this->accountService->all();
$sort = data_get($urlParams, "sort") ?? "-_sys.updatedAt";
$filter = data_get($urlParams, "filter") ?? [];
$arguments = array_merge([
"schema_in" => $this->accountService->currentReadableSchemas(),
"status_in" => ["draft", "published"]
], $filter);
@@ -53,8 +53,6 @@ class HomeController extends Controller
->sort($sort)
->run();
$users = $this->accountService->all();
return ok([
"users" => $users,
"records" => $graph->getRootRecords()->toArray(),
+3 -5
View File
@@ -6,7 +6,6 @@ use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Lucent\Account\AccountService;
use Lucent\Account\AuthService;
use Lucent\Account\Role;
use Lucent\LucentException;
use Lucent\Svelte\Svelte;
use function Lucent\Response\fail;
@@ -31,19 +30,18 @@ class MemberController extends Controller
title: "Members",
data: [
"users" => $this->accountService->allProfiles()->toArray(),
"roles" => Role::cases()
]
);
}
public function invite(Request $request)
{
if (empty($request->input("role"))) {
if (empty($request->input("roles"))) {
return fail("Select a role for the user");
}
try {
$user = $this->authService->invite($request->input("name"), $request->input("email"), $request->input("role"));
$user = $this->authService->invite($request->input("name"), $request->input("email"), $request->input("roles"));
} catch (LucentException $th) {
return fail($th);
}
@@ -56,7 +54,7 @@ class MemberController extends Controller
public function update(Request $request)
{
try {
$this->authService->changeRole($request->input("id"), $request->input("role"));
$this->authService->changeRoles($request->input("id"), $request->input("roles"));
} catch (LucentException $th) {
return fail($th);
}
+66 -24
View File
@@ -6,7 +6,6 @@ use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Lucent\Account\AccountService;
use Lucent\Account\AuthService;
use Lucent\Account\UserRepo;
use Lucent\Channel\ChannelService;
use Lucent\LucentException;
use Lucent\Query\Operator;
@@ -28,7 +27,6 @@ class RecordController extends Controller
private readonly AuthService $authService,
private readonly ChannelService $channelService,
private readonly Svelte $svelte,
private readonly UserRepo $userRepo,
private readonly Query $query,
private readonly Manager $recordManager
)
@@ -38,11 +36,21 @@ class RecordController extends Controller
public function index(Request $request)
{
$schemaName = $request->route("schemaName");
if(!in_array($schemaName,$this->accountService->currentReadableSchemas())){
return $this->svelte->render(
layout: "channel",
view: "recordNotFound",
title: "Schema Not Found",
);
}
$users = $this->accountService->all();
$schema = $this->channelService->getSchema($schemaName)->get();
$urlParams = $request->all();
$sort = data_get($urlParams, "sort") ?? "-_sys.updatedAt";
$filter = data_get($urlParams, "filter") ?? [];
$arguments = array_merge([
"schema" => $schema->name,
"status_in" => "draft,published",
@@ -82,6 +90,7 @@ class RecordController extends Controller
"total" => $graph->total ?? 0,
"filter" => $request->input("filter") ?? [],
"inModal" => true,
"isWritable" => in_array($schemaName,$this->accountService->currentWritableSchemas())
];
if ($request->ajax()) {
@@ -142,6 +151,14 @@ class RecordController extends Controller
public function new(Request $request)
{
if(!in_array($request->input("schema"),$this->accountService->currentWritableSchemas())){
return $this->svelte->render(
layout: "channel",
view: "recordNotFound",
title: "Schema Not Found",
);
}
$schema = $this->channelService->channel->schemas->where("name", $request->input("schema"))->first();
$recordHistory = $this->recordManager->fromSession($request->session())->getRecords();
$record = $this->recordService->createEmpty($schema, $this->authService->currentUserId());
@@ -162,6 +179,14 @@ class RecordController extends Controller
public function newInline(Request $request)
{
if(!in_array($request->input("schema"),$this->accountService->currentWritableSchemas())){
return $this->svelte->render(
layout: "channel",
view: "recordNotFound",
title: "Schema Not Found",
);
}
$schema = $this->channelService->getSchema($request->input("schema"))->get();
$record = $this->recordService->createEmpty($schema);
$queryRecord = QueryRecord::fromRecord($record);
@@ -198,9 +223,17 @@ class RecordController extends Controller
}
$record = $graph->records->first();
if(!in_array($record->schema,$this->accountService->currentReadableSchemas())){
return $this->svelte->render(
layout: "channel",
view: "recordNotFound",
title: "Schema Not Found",
);
}
$schema = $this->channelService->getSchema($record->schema)->get();
$recordHistory = $this->recordManager->fromSession($request->session())->push($rid)->getRecords($rid);
$users = $this->userRepo->all();
return $this->svelte->render(
layout: "channel",
view: "recordEdit",
@@ -209,33 +242,42 @@ class RecordController extends Controller
"schema" => $schema,
"graph" => toArray($graph),
"record" => toArray($record),
"users" => $users,
"users" => $this->accountService->all(),
"recordHistory" => $recordHistory,
"isWritable" => in_array($record->schema,$this->accountService->currentWritableSchemas())
]
);
}
public function editInline(Request $request)
{
$rid = $request->route("rid");
$graph = $this->query
->filter(["id" => $rid])
->limit(1)
->childrenDepth(2)
->parentsDepth(1)
->run();
$record = $graph->records->first();
return ok(
[
"graph" => toArray($graph),
"record" => toArray($record)
]
);
}
// public function editInline(Request $request)
// {
// $rid = $request->route("rid");
//
// $graph = $this->query
// ->filter(["id" => $rid])
// ->limit(1)
// ->childrenDepth(2)
// ->parentsDepth(1)
// ->run();
//
// $record = $graph->records->first();
//
// if(!in_array($record->schema,$this->accountService->currentReadableSchemas())){
// return $this->svelte->render(
// layout: "channel",
// view: "recordNotFound",
// title: "Schema Not Found",
// );
// }
//
// return ok(
// [
// "graph" => toArray($graph),
// "record" => toArray($record)
// ]
// );
// }
public function suggestions(Request $request)
+7 -2
View File
@@ -4,22 +4,27 @@ namespace Lucent\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Lucent\Account\AccountService;
use Lucent\Account\AuthService;
use Lucent\Channel\ChannelService;
readonly class AuthMiddleware
{
public function __construct(private AuthService $authService, private ChannelService $channelService)
public function __construct(
private AccountService $accountService,
private AuthService $authService,
private ChannelService $channelService
)
{
}
public function handle(Request $request, Closure $next)
{
if (!$this->authService->isLoggedIn()) {
return redirect($this->channelService->channel->lucentUrl . "/login");
}
return $next($request);
}
}
+1 -1
View File
@@ -52,7 +52,7 @@ Route::group([
Route::get('/suggestions', [RecordController::class, 'suggestions']);
Route::get('/{rid}', [RecordController::class, 'edit']);
Route::post('/clone/{rid}', [RecordController::class, 'clone']);
Route::get('/editInline/{rid}', [RecordController::class, 'editInline']);
// Route::get('/editInline/{rid}', [RecordController::class, 'editInline']);
Route::get('/{rid}/parents', [RecordController::class, 'parents']);
Route::post('/', [RecordController::class, 'save']);
Route::post('/status/{status}', [RecordController::class, 'status']);
+2
View File
@@ -22,6 +22,8 @@ class CollectionSchema implements Schema
public string $color = "",
public string $titleTemplate = "",
public int $revisions = 0,
public array $read = [],
public array $write = [],
)
{
}
+2
View File
@@ -23,6 +23,8 @@ class FilesSchema implements Schema
public string $color = "",
public string $titleTemplate = "",
public int $revisions = 0,
public array $read = [],
public array $write = [],
)
{
}
+6
View File
@@ -26,6 +26,8 @@ class SchemaService
color: $schemaArr["color"] ?? "",
titleTemplate: $schemaArr["titleTemplate"] ?? "",
revisions: $schemaArr["revisions"] ?? 0,
read: $schemaArr["read"] ?? [],
write: $schemaArr["write"] ?? [],
),
"files" => new FilesSchema(
name: $schemaArr["name"],
@@ -37,6 +39,8 @@ class SchemaService
color: $schemaArr["color"] ?? "",
titleTemplate: $schemaArr["titleTemplate"] ?? "",
revisions: $schemaArr["revisions"] ?? 0,
read: $schemaArr["read"] ?? [],
write: $schemaArr["write"] ?? [],
),
"block" => new BlockSchema(
name: $schemaArr["name"],
@@ -62,6 +66,8 @@ class SchemaService
}
//
// /**
// * @param array<string> $visible
+4 -1
View File
@@ -4,12 +4,14 @@ namespace Lucent\Svelte;
use Illuminate\Contracts\View\Factory;
use Illuminate\Contracts\View\View;
use Lucent\Account\AccountService;
use Lucent\Channel\ChannelService;
class Svelte
{
public function __construct(
public ChannelService $channelService
public ChannelService $channelService,
public AccountService $accountService
)
{
}
@@ -24,6 +26,7 @@ class Svelte
$context["title"] = $title;
$context["data"] = $data;
$context["channel"] = $this->channelService->channel;
$context["readableSchemas"] = $this->accountService->currentReadableSchemas();
$json = json_encode($context);
$divTag = sprintf('<div class="lucent-component" data-layout="%s"></div>', $layout);