1
0
mirror of https://github.com/pocketbase/pocketbase.git synced 2025-04-04 09:49:35 +02:00

added hideControls setting

This commit is contained in:
Gani Georgiev 2022-08-18 17:10:42 +03:00
parent cfaff31d97
commit 7be389704d
9 changed files with 72 additions and 40 deletions

View File

@ -43,6 +43,7 @@ func NewSettings() *Settings {
Meta: MetaConfig{ Meta: MetaConfig{
AppName: "Acme", AppName: "Acme",
AppUrl: "http://localhost:8090", AppUrl: "http://localhost:8090",
HideControls: false,
SenderName: "Support", SenderName: "Support",
SenderAddress: "support@example.com", SenderAddress: "support@example.com",
VerificationTemplate: defaultVerificationTemplate, VerificationTemplate: defaultVerificationTemplate,
@ -270,6 +271,7 @@ func (c S3Config) Validate() error {
type MetaConfig struct { type MetaConfig struct {
AppName string `form:"appName" json:"appName"` AppName string `form:"appName" json:"appName"`
AppUrl string `form:"appUrl" json:"appUrl"` AppUrl string `form:"appUrl" json:"appUrl"`
HideControls bool `form:"hideControls" json:"hideControls"`
SenderName string `form:"senderName" json:"senderName"` SenderName string `form:"senderName" json:"senderName"`
SenderAddress string `form:"senderAddress" json:"senderAddress"` SenderAddress string `form:"senderAddress" json:"senderAddress"`
VerificationTemplate EmailTemplate `form:"verificationTemplate" json:"verificationTemplate"` VerificationTemplate EmailTemplate `form:"verificationTemplate" json:"verificationTemplate"`

View File

@ -174,7 +174,7 @@ func TestSettingsRedactClone(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
expected := `{"meta":{"appName":"test123","appUrl":"http://localhost:8090","senderName":"Support","senderAddress":"support@example.com","verificationTemplate":{"body":"\u003cp\u003eHello,\u003c/p\u003e\n\u003cp\u003eThank you for joining us at {APP_NAME}.\u003c/p\u003e\n\u003cp\u003eClick on the button below to verify your email address.\u003c/p\u003e\n\u003cp\u003e\n \u003ca class=\"btn\" href=\"{ACTION_URL}\" target=\"_blank\" rel=\"noopener\"\u003eVerify\u003c/a\u003e\n\u003c/p\u003e\n\u003cp\u003e\n Thanks,\u003cbr/\u003e\n {APP_NAME} team\n\u003c/p\u003e","subject":"Verify your {APP_NAME} email","actionUrl":"{APP_URL}/_/#/users/confirm-verification/{TOKEN}"},"resetPasswordTemplate":{"body":"\u003cp\u003eHello,\u003c/p\u003e\n\u003cp\u003eClick on the button below to reset your password.\u003c/p\u003e\n\u003cp\u003e\n \u003ca class=\"btn\" href=\"{ACTION_URL}\" target=\"_blank\" rel=\"noopener\"\u003eReset password\u003c/a\u003e\n\u003c/p\u003e\n\u003cp\u003e\u003ci\u003eIf you didn't ask to reset your password, you can ignore this email.\u003c/i\u003e\u003c/p\u003e\n\u003cp\u003e\n Thanks,\u003cbr/\u003e\n {APP_NAME} team\n\u003c/p\u003e","subject":"Reset your {APP_NAME} password","actionUrl":"{APP_URL}/_/#/users/confirm-password-reset/{TOKEN}"},"confirmEmailChangeTemplate":{"body":"\u003cp\u003eHello,\u003c/p\u003e\n\u003cp\u003eClick on the button below to confirm your new email address.\u003c/p\u003e\n\u003cp\u003e\n \u003ca class=\"btn\" href=\"{ACTION_URL}\" target=\"_blank\" rel=\"noopener\"\u003eConfirm new email\u003c/a\u003e\n\u003c/p\u003e\n\u003cp\u003e\u003ci\u003eIf you didn't ask to change your email address, you can ignore this email.\u003c/i\u003e\u003c/p\u003e\n\u003cp\u003e\n Thanks,\u003cbr/\u003e\n {APP_NAME} team\n\u003c/p\u003e","subject":"Confirm your {APP_NAME} new email address","actionUrl":"{APP_URL}/_/#/users/confirm-email-change/{TOKEN}"}},"logs":{"maxDays":7},"smtp":{"enabled":false,"host":"smtp.example.com","port":587,"username":"","password":"******","tls":true},"s3":{"enabled":false,"bucket":"","region":"","endpoint":"","accessKey":"","secret":"******","forcePathStyle":false},"adminAuthToken":{"secret":"******","duration":1209600},"adminPasswordResetToken":{"secret":"******","duration":1800},"userAuthToken":{"secret":"******","duration":1209600},"userPasswordResetToken":{"secret":"******","duration":1800},"userEmailChangeToken":{"secret":"******","duration":1800},"userVerificationToken":{"secret":"******","duration":604800},"emailAuth":{"enabled":true,"exceptDomains":null,"onlyDomains":null,"minPasswordLength":8},"googleAuth":{"enabled":false,"allowRegistrations":true,"clientSecret":"******"},"facebookAuth":{"enabled":false,"allowRegistrations":true,"clientSecret":"******"},"githubAuth":{"enabled":false,"allowRegistrations":true,"clientSecret":"******"},"gitlabAuth":{"enabled":false,"allowRegistrations":true,"clientSecret":"******"}}` expected := `{"meta":{"appName":"test123","appUrl":"http://localhost:8090","hideControls":false,"senderName":"Support","senderAddress":"support@example.com","verificationTemplate":{"body":"\u003cp\u003eHello,\u003c/p\u003e\n\u003cp\u003eThank you for joining us at {APP_NAME}.\u003c/p\u003e\n\u003cp\u003eClick on the button below to verify your email address.\u003c/p\u003e\n\u003cp\u003e\n \u003ca class=\"btn\" href=\"{ACTION_URL}\" target=\"_blank\" rel=\"noopener\"\u003eVerify\u003c/a\u003e\n\u003c/p\u003e\n\u003cp\u003e\n Thanks,\u003cbr/\u003e\n {APP_NAME} team\n\u003c/p\u003e","subject":"Verify your {APP_NAME} email","actionUrl":"{APP_URL}/_/#/users/confirm-verification/{TOKEN}"},"resetPasswordTemplate":{"body":"\u003cp\u003eHello,\u003c/p\u003e\n\u003cp\u003eClick on the button below to reset your password.\u003c/p\u003e\n\u003cp\u003e\n \u003ca class=\"btn\" href=\"{ACTION_URL}\" target=\"_blank\" rel=\"noopener\"\u003eReset password\u003c/a\u003e\n\u003c/p\u003e\n\u003cp\u003e\u003ci\u003eIf you didn't ask to reset your password, you can ignore this email.\u003c/i\u003e\u003c/p\u003e\n\u003cp\u003e\n Thanks,\u003cbr/\u003e\n {APP_NAME} team\n\u003c/p\u003e","subject":"Reset your {APP_NAME} password","actionUrl":"{APP_URL}/_/#/users/confirm-password-reset/{TOKEN}"},"confirmEmailChangeTemplate":{"body":"\u003cp\u003eHello,\u003c/p\u003e\n\u003cp\u003eClick on the button below to confirm your new email address.\u003c/p\u003e\n\u003cp\u003e\n \u003ca class=\"btn\" href=\"{ACTION_URL}\" target=\"_blank\" rel=\"noopener\"\u003eConfirm new email\u003c/a\u003e\n\u003c/p\u003e\n\u003cp\u003e\u003ci\u003eIf you didn't ask to change your email address, you can ignore this email.\u003c/i\u003e\u003c/p\u003e\n\u003cp\u003e\n Thanks,\u003cbr/\u003e\n {APP_NAME} team\n\u003c/p\u003e","subject":"Confirm your {APP_NAME} new email address","actionUrl":"{APP_URL}/_/#/users/confirm-email-change/{TOKEN}"}},"logs":{"maxDays":7},"smtp":{"enabled":false,"host":"smtp.example.com","port":587,"username":"","password":"******","tls":true},"s3":{"enabled":false,"bucket":"","region":"","endpoint":"","accessKey":"","secret":"******","forcePathStyle":false},"adminAuthToken":{"secret":"******","duration":1209600},"adminPasswordResetToken":{"secret":"******","duration":1800},"userAuthToken":{"secret":"******","duration":1209600},"userPasswordResetToken":{"secret":"******","duration":1800},"userEmailChangeToken":{"secret":"******","duration":1800},"userVerificationToken":{"secret":"******","duration":604800},"emailAuth":{"enabled":true,"exceptDomains":null,"onlyDomains":null,"minPasswordLength":8},"googleAuth":{"enabled":false,"allowRegistrations":true,"clientSecret":"******"},"facebookAuth":{"enabled":false,"allowRegistrations":true,"clientSecret":"******"},"githubAuth":{"enabled":false,"allowRegistrations":true,"clientSecret":"******"},"gitlabAuth":{"enabled":false,"allowRegistrations":true,"clientSecret":"******"}}`
if encodedStr := string(encoded); encodedStr != expected { if encodedStr := string(encoded); encodedStr != expected {
t.Fatalf("Expected %v, got \n%v", expected, encodedStr) t.Fatalf("Expected %v, got \n%v", expected, encodedStr)

Binary file not shown.

View File

@ -10,7 +10,7 @@
import Toasts from "@/components/base/Toasts.svelte"; import Toasts from "@/components/base/Toasts.svelte";
import Toggler from "@/components/base/Toggler.svelte"; import Toggler from "@/components/base/Toggler.svelte";
import Confirmation from "@/components/base/Confirmation.svelte"; import Confirmation from "@/components/base/Confirmation.svelte";
import { pageTitle, appName } from "@/stores/app"; import { pageTitle, appName, hideControls } from "@/stores/app";
import { admin } from "@/stores/admin"; import { admin } from "@/stores/admin";
import { setErrors } from "@/stores/errors"; import { setErrors } from "@/stores/errors";
import { resetConfirmation } from "@/stores/confirmation"; import { resetConfirmation } from "@/stores/confirmation";
@ -20,7 +20,7 @@
let showAppSidebar = false; let showAppSidebar = false;
$: if ($admin?.id) { $: if ($admin?.id) {
loadAppName(); loadSettings();
} }
function handleRouteLoading(e) { function handleRouteLoading(e) {
@ -42,18 +42,19 @@
replace("/"); replace("/");
} }
async function loadAppName() { async function loadSettings() {
if (!$admin?.id) { if (!$admin?.id) {
return; return;
} }
try { try {
const settings = await ApiClient.settings.getAll({ const settings = await ApiClient.settings.getAll({
$cancelKey: "loadAppName", $cancelKey: "initialAppSettings",
}); });
$appName = settings?.meta?.appName || ""; $appName = settings?.meta?.appName || "";
$hideControls = !!settings?.meta?.hideControls;
} catch (err) { } catch (err) {
console.warn("Failed to load app name.", err); console.warn("Failed to load app settings.", err);
} }
} }

View File

@ -1,4 +1,5 @@
<script> <script>
import { hideControls } from "@/stores/app";
import { collections, activeCollection } from "@/stores/collections"; import { collections, activeCollection } from "@/stores/collections";
import CollectionUpsertPanel from "@/components/collections/CollectionUpsertPanel.svelte"; import CollectionUpsertPanel from "@/components/collections/CollectionUpsertPanel.svelte";
@ -63,12 +64,14 @@
{/each} {/each}
</div> </div>
<footer class="sidebar-footer"> {#if !$hideControls}
<button type="button" class="btn btn-block btn-outline" on:click={() => collectionPanel?.show()}> <footer class="sidebar-footer">
<i class="ri-add-line" /> <button type="button" class="btn btn-block btn-outline" on:click={() => collectionPanel?.show()}>
<span class="txt">New collection</span> <i class="ri-add-line" />
</button> <span class="txt">New collection</span>
</footer> </button>
</footer>
{/if}
</aside> </aside>
<CollectionUpsertPanel bind:this={collectionPanel} /> <CollectionUpsertPanel bind:this={collectionPanel} />

View File

@ -7,7 +7,7 @@
loadCollections, loadCollections,
} from "@/stores/collections"; } from "@/stores/collections";
import tooltip from "@/actions/tooltip"; import tooltip from "@/actions/tooltip";
import { pageTitle } from "@/stores/app"; import { pageTitle, hideControls } from "@/stores/app";
import PageWrapper from "@/components/base/PageWrapper.svelte"; import PageWrapper from "@/components/base/PageWrapper.svelte";
import Searchbar from "@/components/base/Searchbar.svelte"; import Searchbar from "@/components/base/Searchbar.svelte";
import RefreshButton from "@/components/base/RefreshButton.svelte"; import RefreshButton from "@/components/base/RefreshButton.svelte";
@ -68,15 +68,19 @@
<div class="icon"> <div class="icon">
<i class="ri-database-2-line" /> <i class="ri-database-2-line" />
</div> </div>
<h1 class="m-b-10">Create your first collection to add records!</h1> {#if $hideControls}
<button <h1 class="m-b-10">You don't have any collections yet.</h1>
type="button" {:else}
class="btn btn-expanded-lg btn-lg" <h1 class="m-b-10">Create your first collection to add records!</h1>
on:click={() => collectionUpsertPanel?.show()} <button
> type="button"
<i class="ri-add-line" /> class="btn btn-expanded-lg btn-lg"
<span class="txt">Create new collection</span> on:click={() => collectionUpsertPanel?.show()}
</button> >
<i class="ri-add-line" />
<span class="txt">Create new collection</span>
</button>
{/if}
</div> </div>
</PageWrapper> </PageWrapper>
{:else} {:else}
@ -90,14 +94,16 @@
</nav> </nav>
<div class="inline-flex gap-5"> <div class="inline-flex gap-5">
<button {#if !$hideControls}
type="button" <button
class="btn btn-secondary btn-circle" type="button"
use:tooltip={{ text: "Edit collection", position: "right" }} class="btn btn-secondary btn-circle"
on:click={() => collectionUpsertPanel?.show($activeCollection)} use:tooltip={{ text: "Edit collection", position: "right" }}
> on:click={() => collectionUpsertPanel?.show($activeCollection)}
<i class="ri-settings-4-line" /> >
</button> <i class="ri-settings-4-line" />
</button>
{/if}
<RefreshButton on:refresh={() => recordsList?.load()} /> <RefreshButton on:refresh={() => recordsList?.load()} />
</div> </div>

View File

@ -1,8 +1,9 @@
<script> <script>
import ApiClient from "@/utils/ApiClient"; import ApiClient from "@/utils/ApiClient";
import CommonHelper from "@/utils/CommonHelper"; import CommonHelper from "@/utils/CommonHelper";
import { pageTitle, appName } from "@/stores/app"; import { pageTitle, appName, hideControls } from "@/stores/app";
import { addSuccessToast } from "@/stores/toasts"; import { addSuccessToast } from "@/stores/toasts";
import tooltip from "@/actions/tooltip";
import PageWrapper from "@/components/base/PageWrapper.svelte"; import PageWrapper from "@/components/base/PageWrapper.svelte";
import Field from "@/components/base/Field.svelte"; import Field from "@/components/base/Field.svelte";
import SettingsSidebar from "@/components/settings/SettingsSidebar.svelte"; import SettingsSidebar from "@/components/settings/SettingsSidebar.svelte";
@ -54,6 +55,7 @@
function init(settings = {}) { function init(settings = {}) {
$appName = settings?.meta?.appName; $appName = settings?.meta?.appName;
$hideControls = !!settings?.meta?.hideControls;
formSettings = { formSettings = {
meta: settings?.meta || {}, meta: settings?.meta || {},
@ -108,6 +110,20 @@
<input type="number" id={uniqueId} required bind:value={formSettings.logs.maxDays} /> <input type="number" id={uniqueId} required bind:value={formSettings.logs.maxDays} />
</Field> </Field>
<Field class="form-field form-field-toggle" name="meta.hideControls" let:uniqueId>
<input type="checkbox" id={uniqueId} bind:checked={formSettings.meta.hideControls} />
<label for={uniqueId}>
<span class="txt">Hide collection create and edit controls</span>
<i
class="ri-information-line link-hint"
use:tooltip={{
text: `This is useful to prevent making accidental schema changes on a production environment.`,
position: "right",
}}
/>
</label>
</Field>
<div class="col-lg-12 flex"> <div class="col-lg-12 flex">
<div class="flex-fill" /> <div class="flex-fill" />
{#if hasChanges} {#if hasChanges}

View File

@ -4,7 +4,7 @@
import ApiClient from "@/utils/ApiClient"; import ApiClient from "@/utils/ApiClient";
import CommonHelper from "@/utils/CommonHelper"; import CommonHelper from "@/utils/CommonHelper";
import tooltip from "@/actions/tooltip"; import tooltip from "@/actions/tooltip";
import { pageTitle } from "@/stores/app"; import { pageTitle, hideControls } from "@/stores/app";
import PageWrapper from "@/components/base/PageWrapper.svelte"; import PageWrapper from "@/components/base/PageWrapper.svelte";
import Searchbar from "@/components/base/Searchbar.svelte"; import Searchbar from "@/components/base/Searchbar.svelte";
import RefreshButton from "@/components/base/RefreshButton.svelte"; import RefreshButton from "@/components/base/RefreshButton.svelte";
@ -116,14 +116,16 @@
<div class="breadcrumb-item">{$pageTitle}</div> <div class="breadcrumb-item">{$pageTitle}</div>
</nav> </nav>
<button {#if !$hideControls}
type="button" <button
class="btn btn-secondary btn-circle" type="button"
use:tooltip={{ text: "Edit profile collection", position: "right" }} class="btn btn-secondary btn-circle"
on:click={() => collectionUpsertPanel?.show(profileCollection)} use:tooltip={{ text: "Edit profile collection", position: "right" }}
> on:click={() => collectionUpsertPanel?.show(profileCollection)}
<i class="ri-settings-4-line" /> >
</button> <i class="ri-settings-4-line" />
</button>
{/if}
<RefreshButton on:refresh={() => loadUsers()} /> <RefreshButton on:refresh={() => loadUsers()} />

View File

@ -3,3 +3,5 @@ import { writable } from "svelte/store";
export const pageTitle = writable(''); export const pageTitle = writable('');
export const appName = writable(''); export const appName = writable('');
export const hideControls = writable(false);