You have been redirected from an outdated version of the article. Below is the content available on this topic. To view the old article click here.

me

The keyword me contains a reference to the current user. If the current request is made in a anonymous context me returns an empty result.

The keyword can be used in all Template Expressions, in Hide and show Conditions etc. It is often used to show/hide individual parts of a page depending on the permissions / status of a user etc.

Example

Usage of the me keyword

Search results for "me"

merge()

Merges collections and objects into a single collection. This method can be used to add entities to a collection, or to merge multiple collections into a single one. All objects that are passed to this function will be added to the resulting collection. If a parameter is a collection, all objects of that collection are added to the resulting collection as well.

This method will not remove duplicate entries. Use merge_unique() for that.

merge(object...)

timer()

This method can be used to measure the performance of sections of code. The action parameter can be start to create a new timer or get to retrieve the elapsed time (in milliseconds) since the start of the timer.

timer(name, action)

mail_add_attachment()

Adds a file (instances of File) as an attachment to the mail. The name parameter can be used to send the file with a different name than the filename in the Structr filesystem.

mail_add_attachment(fileNode [, name] )

mail_save_outgoing_message()

Configures the Advanced Mail Module that the next invocation of mail_send() should save the outgoing email as an EMailMessage node.
Configured attachments are copied and attached to the EMailMessage node. For attached dynamic files the evaluated result is saved as a static file.
After the mail_send() command is finished, the outgoing message can be accessed via mail_get_last_outgoing_message().

mail_save_outgoing_message(boolean)

merge_unique()

Merges collections and objects into a single collection, removing duplicates. This method is very similar to merge() except that the resulting collection will not contain duplicate entries.

merge_unique(object...)

parameter_map

The parameter_map returns a map (String, String) containing all request parameters of the current request.

parameterMap is an alias for this keyword.

methodParameters

Returns all method parameters in schema methods as a map.

mail_get_last_outgoing_message()

Returns the outgoing message as the saved EMailMessage node.

mail_get_last_outgoing_message()

mail_clear_attachments()

Clears the attachments from the current mail.

mail_clear_attachments()

me

The keyword me contains a reference to the current user.

The keyword can be used in all Template Expressions, in Hide and show Conditions etc. It is often used to show/hide individual parts of a page depending on the permissions / status of a user etc.

complement()

Returns the result of removing the removeObjects from sourceList.

complement(sourceList, removeObjects...)

merge_properties()

Copies the values for the given list of property keys from the source entity to the target entity.

merge_properties(source, target, propertyKeys...)

Relationship Details Dialog

The Cascading Delete settings allow configuration of what happens when either end of the relationship is deleted. The possible values are explained in-depth in the help popup in the dialog.
Automatic Creation of Related Nodes configures if it is allowed to include nested nodes in a REST POST request for this relationship. A node with the given property set is automatically created and linked to the node. If the nested node contains an id attribute (or another property marked as unique) a node is searched for that property and linked if found.

Permission Resolution allows configuration of rights propagation in the graph. If NONE is selected, no rights propagation is applied for this relationship. If SOURCE_TO_TARGET is selected the rights are propagated along the relationship direction to the next node. For TARGET_TO_SOURCE the rights propagation is works against the relationship direction. For ALWAYS the direction of the relationship does not matter and rights propagation is always applied.
Specific rights (Read, Write, Delete, AccessControl) can be added, kept or removed according to the propagation configuration. If a user has read rights to the previous node and Read is configured to Keep, the user also has read rights for the next node. (Specific User/Group rights are applied before using permission propagation - i.e. if a user has specific rights configured for a node, permission resolution is not evaluated as user rights are more specific).
Along the way of permission propagation, properties can be hidden in order to hide sensitive information from users who get rights from permission propagation. The property names can be separated by comma , or space character.

There are 3 tabs where the functionality of the type can be configured:

  • Local Attributes
    A Custom Type can be extended with dynamic properties to provide the data model for the intended use-case. This list contains all local properties (meaning they are defined on this type directly).
  • Views
    The properties of a type can be combined into named Views, which are accessible under an individual REST URL. Access to these URLs can be configured independently for each HTTP method using Resource Access Grants, which makes them an ideal tool to create specialised endpoints for different client applications (e.g. mobile clients etc.).
  • Methods
    There are different kinds of methods - callback methods and entity methods. Callback methods are automatically executed by the framework upon certain lifecycle events and have a strict naming convention. Entity methods are called by the user/programmer.
    Entity methods are not automatically run by the framework and must be called manually. This either means making a POST request to /structr/rest/(Type)/(<a data-id="7c9c8218bced42bab66868373e64d885" class="mention">UUID</a>)/(methodName) or in serverside JavaScript as node.methodName();

Type Details Dialog

There are 5 tabs where the functionality of the type can be configured:

  • Local Attributes
    A Custom Type can be extended with dynamic properties to provide the data model for the intended use-case. This list contains all local properties (meaning they are defined on this type directly).

  • Views
    The properties of a type can be combined into named Views, which are accessible under an individual REST URL. Access to these URLs can be configured independently for each HTTP method using Resource Access Grants, which makes them an ideal tool to create specialised endpoints for different client applications (e.g. mobile clients etc.).

  • Methods
    There are different kinds of methods - callback methods and entity methods. Callback methods are automatically executed by the framework upon certain lifecycle events and have a strict naming convention. Entity methods are called by the user/programmer.
    Entity methods are not automatically run by the framework and must be called manually. This either means making a POST request to /structr/rest/<Type>/<UUID>/<methodName> or in serverside JavaScript as <node>.<methodName>();

  • Remote Attributes
    Custom types can be linked to other types, including base types. Structr will automatically create Remote Attributes on either side of the link, which can then be used to create, update or remove the relationships between instances of the two types. In this tab the configuration of the remote attributes can be viewed and edited. The names configured here are used throughout the application to refer to the other side of the relationship(s).

  • Inherited Attributes
    The content of this tab is of informative character. All inherited attributes are shown with an information from where it was inherited.

Example Implementation

<div id="qrimage-wrapper">
<img id="qrimage" style="margin-left: calc(50% - 100px);">
<div>To use two factor authentication, scan this QR code with an authenticator app on your smartphone.</div>

<div class="text-sm mt-6">
<div>
<b>Android: </b>
<a class="cursor-pointer hover:text-blue-400 text-blue-700" href="https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2&hl=en&gl=US">Google Authenticator</a> on Google Playstore
</div>
<div>
<b>Apple iOS: </b>
<a class="cursor-pointer hover:text-blue-400 text-blue-700" href="https://apps.apple.com/us/app/google-authenticator/id388497605">Google Authenticator</a> on App Store
</div>
</div>
</div>

<form action="#" id="twoFactorForm">
<input id="twoFactorToken" type="hidden" value="${request.token}">
<div class="my-6">
<label class="block text-sm font-medium leading-5 text-gray-700">Two Factor Code</label>
<input id="twoFactorCode" class="appearance-none block w-full px-3 py-2 bg-blue-100 border border-gray-300 rounded-md placeholder-gray-400 focus:outline-none focus:shadow-outline-blue focus:border-blue-300 transition duration-150 ease-in-out sm:text-sm sm:leading-5">
</div>
<button type="submit" id="login-button" class="w-full flex justify-center py-2 px-4 border border-transparent text-sm font-medium rounded-md text-white bg-blue-600 hover:bg-blue-500 focus:outline-none focus:border-blue-700 focus:shadow-outline-indigo active:bg-blue-700 transition duration-150 ease-in-out">Login</button>
</form>

<script>
document.addEventListener('DOMContentLoaded', () => {

// get all required elements
const qrimageWrapper = document.getElementById('qrimage-wrapper');
const token = document.getElementById('twoFactorToken').value;
const codeInput = document.getElementById('twoFactorCode').value;
const loginButton = document.getElementById('login-button');

let qrdata = (new URLSearchParams(location.search)).get('qrdata');

if (!qrdata) {
// remove qr code placeholder if user is not shown qr code
qrimageWrapper.remove();
} else {
// transform url-safe qr code to regular base64 to display as image
qrimageWrapper.querySelector('#qrimage').src = 'data:image/png;base64, ' + qrdata.replaceAll('_', '/').replaceAll('-', '+');
}

document.getElementById('twoFactorForm').addEventListener('submit', async (event) => {

event.preventDefault();

loginButton.disabled = true;

const response = await fetch('/structr/rest/login', {
method: 'POST',
body: JSON.stringify({
twoFactorToken: token,
twoFactorCode: codeInput
})
});

if (response.ok) {

loginButton.textContent = 'Login successful';
window.location.href = '/';

} else {

let buttonText = 'Login failed - is device time correct?';

let reason = response.headers.get('reason');

if (reason === 'wrongTwoFactorCode') {
buttonText = 'Two Factor Code is not correct';
}

loginButton.disabled = false;
loginButton.textContent = buttonText;

window.setTimeout(function() {
loginButton.textContent = 'Login';
loginButton.disabled = false;
}, 2000);
}
});
});
</script>

Execution Flow

When a flow is called, execution will begin at it’s starting element. In case of no entry point being set or no elements being contained, the flow will simply return a null result. Otherwise the Flow Engine will evaluate the starting element and then proceed with the evaluation of the element connected to the initial element as next element. This continues until there is no more next element available, an error has been thrown or the evaluated element is a Return element, in which case the result of the element’s evaluation will be returned. Although this sums up the general execution flow of a Flow, there are notable exceptions to this in the form of specialized elements. For example a Decision element will branch into two execution paths and choose one based on the connected conditional elements. Likewise, a Loop element will enter a different execution path for each element and finally proceeds with the original next element.

Changelog

{"time":1455195862431,"userId":"f02e59a47d[...]","userName":"admin","verb":"change","key":"name","prev":null,"val":"My new name"}
{"time":1455195903852,"userId":"f02e59a47d[...]","userName":"admin","verb":"change","key":"name","prev":"My new name","val":"New Name"}
{"time":1455196049579,"userId":"f02e59a47d[...]","userName":"admin","verb":"link","rel":"has","relId":"97d26b5778[...]026eb615e","relDir":"out","target":"4e32a9f6eb[...]bd7a6a"}
{"time":1455195961348,"userId":"f02e59a47d[...]","userName":"admin","verb":"unlink","rel":"has","relId":"97d26b5778[...]026eb615e","relDir":"out","target":"4e32a9f6eb[...]bd7a6a"}
{"time":1455196115875,"userId":"00000000000000000000000000000000","userName":"superadmin","verb":"unlink","rel":"OWNS","relId":"b29e98329fb[...]864aa038","relDir":"out","target":"f02e59a47d[...]"}

onAcmeChallenge

This method is called when an ACME challenge of type dns is triggered, typically by using the maintenance method letsencrypt like this:

$.maintenance('letsencrypt', { server: 'production', challenge: 'dns', wait: '60', reload: true });

The primary use case of this method is creating a DNS TXT record via an external API call to a DNS provider.

The following parameters are passed to this method and can be retrieved with $.retrieve(key):

Parameter name Description Examples
type The type of the ACME authorisation challenge dns
domain The domain the ACME challenge is created for subdomain.example.com
record The name of the DNS record including prefix _acme-challenge. and suffix . _acme-challenge.subdomain.example.com.
digest The token string that is probed by the ACME server to validate the challenge X6Rea0DdZ-5XGotp1geAxfsdDR0x1T9d_kAseA4YMCA