(core) add SELF_HYPERLINK() function for generating links to the current document

Summary:
 * Adds a `SELF_HYPERLINK()` python function, with optional keyword arguments to set a label, the page, and link parameters.
 * Adds a `UUID()` python function, since using python's uuid.uuidv4 hits a problem accessing /dev/urandom in the sandbox.  UUID makes no particular quality claims since it doesn't use an audited implementation.  A difficult to guess code is convenient for some use cases that `SELF_HYPERLINK()` enables.

The canonical URL for a document is mutable, but older versions generally forward.  So for implementation simplicity the document url is passed it on sandbox creation and remains fixed throughout the lifetime of the sandbox.  This could and should be improved in future.

The URL is passed into the sandbox as a `DOC_URL` environment variable.

The code for creating the URL is factored out of `Notifier.ts`. Since the url is a function of the organization as well as the document, some rejiggering is needed to make that information available to DocManager.

On document imports, the new document is registered in the database slightly earlier now, in order to keep the procedure for constructing the URL in different starting conditions more homogeneous.

Test Plan: updated test

Reviewers: dsagal

Reviewed By: dsagal

Differential Revision: https://phab.getgrist.com/D2759
This commit is contained in:
Paul Fitzpatrick
2021-03-18 18:40:02 -04:00
parent b4c34cedad
commit 0c5f7cf0a7
14 changed files with 207 additions and 39 deletions

View File

@@ -155,6 +155,7 @@ export interface DocAuthResult {
removed: boolean|null; // Set if the doc is soft-deleted. Users may still have access
// to removed documents for some purposes. Null on error.
error?: ApiError;
cachedDoc?: Document; // For cases where stale info is ok.
}
// Represent a DocAuthKey as a string. The format is "<urlId>:<org> <userId>".
@@ -932,7 +933,8 @@ export class HomeDBManager extends EventEmitter {
if (!forkId) { throw new ApiError('invalid document identifier', 400); }
// We imagine current user owning trunk if there is no embedded userId, or
// the embedded userId matches the current user.
const access = (forkUserId === undefined || forkUserId === userId) ? 'owners' : null;
const access = (forkUserId === undefined || forkUserId === userId) ? 'owners' :
(userId === this.getPreviewerUserId() ? 'viewers' : null);
if (!access) { throw new ApiError("access denied", 403); }
doc = {
name: 'Untitled',
@@ -3854,7 +3856,7 @@ export async function makeDocAuthResult(docPromise: Promise<Document>): Promise<
try {
const doc = await docPromise;
const removed = Boolean(doc.removedAt || doc.workspace.removedAt);
return {docId: doc.id, access: doc.access, removed};
return {docId: doc.id, access: doc.access, removed, cachedDoc: doc};
} catch (error) {
return {docId: null, access: null, removed: null, error};
}