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.
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.
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.
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.
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.
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 |