Some parts of this page have not been translated, sorry for the inconvenience

Documentatie


/*

# Quick Start

Every plugin consists of three files:
 - main.html - plugin html template (uses [Mustache](https://mustache.github.io/) templating engine).
 - main.js - contains plugin configuration and logic while in builder (uses JavaScript ES5 version).
 - main.png - plugin icon that will be displayed in builders UI (size 24x24 pixels).

This is sample code from Vimeo video plugin:
[
```html
<iframe id="{{id}}_vimeo" src="http://player.vimeo.com/video/{{content.groupId}}?portrait={{content.portrait}}&title={{content.title}}&autoplay={{content.autoplay}}&color={{content.color}}" width="{{width}}px" height="{{height}}px" frameborder="0" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>
```

```javascript
PluginWrapper.registerPlugin('vimeo', {
	name: 'Vimeo',
	element: {
		minSize: {width: 100, height: 100},
		defaultSize: {width: 480, height: 300},
		resizable: true
	},
	propertyDialog: {
		noScroll: true,
		tabs: [
			{children: [
				{type: 'VerticalLayout', children: [
					{type: 'Label', text: __('Vimeo ID or URL'),
						helpText: __('Vimeo video id or url with format https://vimeo.com/{video_id}')
					},
					{type: 'TextField', id: 'groupId'}
				]},
				{type: 'HorizontalLayout', css: {marginTop: 15}, children: [
					{type: 'VerticalLayout', children: [
						{type: 'Label', text: __('Portrait'), helpText: __('Show the user’s portrait on the video')},
						{type: 'RadioBox', id: 'portrait0', label: __('Yes'), group: 'portrait'},
						{type: 'RadioBox', id: 'portrait1', label: __('No'), group: 'portrait'}
					]},
					{type: 'VerticalLayout', children: [
						{type: 'Label', text: __('Title'), helpText: __('Show the title on the video.')},
						{type: 'RadioBox', id: 'title0', label: __('Yes'), group: 'title'},
						{type: 'RadioBox', id: 'title1', label: __('No'), group: 'title'}
					]},
					{type: 'VerticalLayout', children: [
						{type: 'Label', text: __('Autoplay'), helpText: __('Play the video automatically on load.')},
						{type: 'RadioBox', id: 'autoplay0', label: __('Yes'), group: 'autoplay'},
						{type: 'RadioBox', id: 'autoplay1', label: __('No'), group: 'autoplay'}
					]},
					{type: 'VerticalLayout', children: [
						{type: 'Label', text: __('Color'), helpText: __('Specify the color of the video controls.')},
						{type: 'ColorSelector', id: 'color'}
					]}
				]}
			]}
		]
	},
	resizeTimeout: null,
	resizeAction: function(data, elem) {
		if (!this.resizeTimeout) {
			var self = this;
			this.resizeTimeout = setTimeout(function () {
				self.resizeTimeout = null;
				self.updateElement();
			}, 1000);
		}
	},
	openAction: function(fields, data, elem) {
		fields.groupId.setText(data.content.groupId);
		fields.portrait0.setValue(data.content.portrait === '1');
		fields.portrait1.setValue(data.content.portrait === '0');

		fields.title0.setValue(data.content.title === '1');
		fields.title1.setValue(data.content.title === '0');

		fields.autoplay0.setValue(data.content.autoplay === '1');
		fields.autoplay1.setValue(data.content.autoplay === '0');

		fields.color.setValue(data.content.color);

		fields.groupId.on('change', function() {
			var url = fields.groupId.getText();
			var regExp = /https:\/\/(www\.)?vimeo.com\/(\d+)($|\/)/;
			var match = url.match(regExp);
			if (match) {
				fields.groupId.setText(match[2]);
			}
		});
	},
	applyAction: function(fields, data, elem) {
		if (fields.portrait0.getValue()) data.content.portrait = '1';
		if (fields.portrait1.getValue()) data.content.portrait = '0';

		if (fields.title0.getValue()) data.content.title = '1';
		if (fields.title1.getValue()) data.content.title = '0';

		if (fields.autoplay0.getValue()) data.content.autoplay = '1';
		if (fields.autoplay1.getValue()) data.content.autoplay = '0';

		data.content.color = fields.color.getValue().replace('#', '');
		data.content.groupId = fields.groupId.getText();
	},
	loadAction: function (data) {
		if (!data.content.groupId) data.content.groupId = '7976699';
		if (!data.content.portrait) data.content.portrait = '1';
		if (!data.content.title) data.content.title = '1';
		if (!data.content.autoplay) data.content.autoplay = '0';
		if (!data.content.color) data.content.color = '#00adef';
	}
});
```
]


 ## Template (main.html)

Here should be html code (with [Mustache](https://mustache.github.io/) templating engine)
that will be rendered to builder and final published site.
If you need different html for different cases (when in builder, on preview or on plublish) there
variables added to template for that:
 - isPublished - will be true when plugin is rendered on published/previewed in builder website.
 - isPreview - will be true when plugin is rendered on previewed in builder website.

Template will also be populated with properties from plugins data object (@see PluginDataObject).


## Plugin render and configuration logic (main.js)

Here should be plugin registration script, that defines plugins property dialog and all logic
how configuration is saved, loaded from to property dialog (@see PluginWrapper.registerPlugin).


# API Specification

## Plugin registration

```javascript
*/


interface PluginWrapper {

	/**
	 * Plugin element factory.
	 * Usage: var elem = PluginWrapper.PluginElement.fromData(data);
	 */
	PluginElement: {
		/** Create element from saved data object. */
		fromData: (data: object) => PluginElement|null
	};

	/**
	 * Register plugin
	 * @param {string} id unique plugin identifier (use standard variable naming conventions).
	 * @param {PluginDefinition} definition plugin definition object.
	 */
	registerPlugin(id: string, definition: PluginDefinition): void;

	/**
	 * Add event listener
	 * Available events:
	 *   builder.init - triggered when builder is fully initialized;
	 *   page.beforeload - triggered before page content starts loading;
	 *   page.load - triggered after page content is loaded;
	 *   templates.show - triggered when template chooser is shown;
	 *   templates.hide - triggered when template chooser is hidden;
	 *   contextmenu.show - triggered before context menu is shown (useful for modifying context menu);
	 * @param event event type.
	 * @param func event handler function.
	 */
	addEventListener(event: string, func: (e?: PluginEvent) => void): void;

	/**
	 * Remove event listener
	 * @param event event type.
	 * @param func event handler function.
	 */
	removeEventListener(event: string, func: (e?: PluginEvent) => void): void;

}

/** Builder event descriptor. */
interface PluginEvent {
	
	/** Horizontal mouse pointer position relative to website 'Body'.*/
	x: number;
	
	/** Vertical mouse pointer position relative to website 'Body'.*/
	y: number;
	
	/** Context menu descriptor (available only for context menu events). */
	ctxMenu: PluginContextMenu|null;
	
	/** Target element of this event (can be null). */
	target: PluginBody|PluginContent|PluginElement|null;
	
	/** Target elements list for cases when event target is multiple element selection (can be undefined). */
	targets?: PluginElement[];
}

/**
 * With this you define your plugin.
 */
interface PluginDefinition {
	
	/**
	 * Display name of the plugin. This is the name that will be visible in plugin toolbar in builder.
	 * (required)
	 */
	name: string;
	
	/**
	 * Rendered element options. They define how element will be rendered in builder.
	 * (required)
	 */
	element: PluginElementOptions;
	
	/**
	 * Property dialog definition object. This defines how property/configuration dialog will look.
	 * (required)
	 */
	propertyDialog: PropertyDialogDefinition;
	
	/**
	 * Custom object that (its instance) will be shared between different instances of this plugin.
	 * You can use this to make sure something (ex. external script loaded) is initialised only once.
	 * (optional)
	 */
	pluginScoped: Object;
	
	/**
	 * Callback that will be called when element is being resized.
	 * This callback will be called in realtime (a lots of times), so do not do anything heavy in it.
	 * (optional)
	 * @param data plugin data object that describes your plugin.
	 * @param elem plugins content root element (in this element your plugins template will be rendered).
	 */
	resizeAction: (data: PluginDataObject, elem: jQueryElement) => void;
	
	/**
	 * Callback that will be called before opening property dialog.
	 * It is usually used to load values prom data object to property dialogs fields.
	 * (optional)
	 * @param fields object with controls with reference ids from property dialog.
	 * @param data plugin data object that describes your plugin.
	 * @param elem plugins content root element (in this element your plugins template will be rendered).
	 */
	openAction: (fields: FieldsIndex, data: PluginDataObject, elem: jQueryElement) => void;
	
	/**
	 * Callback that will be called when apply button in property dialog is clicked.
	 * It is usually used to store values from property dialogs fields to data object.
	 * (optional)
	 * @param fields object with controls with reference ids from property dialog.
	 * @param data plugin data object that describes your plugin.
	 * @param elem plugins content root element (in this element your plugins template will be rendered).
	 */
	applyAction: (fields: FieldsIndex, data: PluginDataObject, elem: jQueryElement) => void;
	
	/**
	 * Callback that will be called when user saves website in builder.
	 * This is the last chance to modify data object before it will be serialized and saved.
	 * (optional)
	 * @param data plugin data object that describes your plugin.
	 * @param elem plugins content root element (in this element your plugins template will be rendered).
	 */
	saveAction: (data: PluginDataObject, elem: jQueryElement) => void;
	
	/**
	 * Callback that will be called when plugin elements data is loaded.
	 * This is perfect place to set default values (if they not already set or this is new element).
	 * (optional)
	 * @param data plugin data object that describes your plugin.
	 */
	loadAction: (data: PluginDataObject) => void;
	
	/**
	 * Callback that will be called when HTML DOM elements of this element are initialised.
	 * You can do some initial html element initialisation processing.
	 * (optional)
	 * @param data plugin data object that describes your plugin.
	 * @param elem plugins content root element (in this element your plugins template will be rendered).
	 */
	loadedAction: (data: PluginDataObject, elem: jQueryElement) => void;
	
}

/**
 * This is plugin data object, it contains data about your plugin element.
 */
interface PluginDataObject {
	/** Unique id of the element. (read-only) */
	id: string;
	/** Element X position in pixels. (read-only) */
	x: number;
	/** Element Y position in pixels. (read-only) */
	y: number;
	/** Element width in pixels. (read-only) */
	width: number;
	/** Element height in pixels. (read-only) */
	height: number;
	/** Element Z index. (read-only) */
	zIndex: number;
	/**
	 * Custon object that will contain all plugin element specific options.
	 * This is where you store data (as object properties) about your plugin.
	 * Data should consist only of scalar values and plain objects.
	 */
	content: Object;
}

/** jQuery element with all its jquery stuff. */
interface jQueryElement {}

/**
 * This object defines how element will be rendered in builder.
 * (All properties in this object are optional)
 */
interface PluginElementOptions {
	/**
	 * If true plugin element in builder will only be rendered once.
	 * If false it will be re-rendered on every update call.
	 * (default: false)
	 */
	renderOnce: boolean;
	/**
	 * If true content of the plugin element will be rendered in iframe.
	 * This can be used to prevent element plugins external scripts from interfering with builder.
	 * (default: false)
	 */
	isolatedRendering: boolean;
	/** Minimum plugin element size definition. Users will not be able to make element smaller than this size. */
	minSize: Size;
	/** Default plugin element size definition. This is the size element will be when added to builder. */
	defaultSize: Size;
	/** If true then users will be able to resize element in builder. (default: true) */
	resizable: boolean;
	/** If true then users will see "Full screen width" option in element options dialog. (default: true) */
	supportsFullWidth: boolean;
}

/**
 * This object define size.
 * (All properties in this object are optional)
 */
interface Size {
	width: number;
	height: number;
}

/**
 * This object defined dialog size.
 * (All properties in this object are optional)
 */
interface DialogSize extends Size {
	/** Min content height, content will not be smaller height, event if there is no content to fill it. */
	minBodyHeight: number;
	/** Max content height after witch scroll will appiear. */
	maxBodyHeight: number;
}

/**
 * Property dialog definition object.
 * (All properties in this object are optional)
 */
interface PropertyDialogDefinition {
	/** Dialog window size. */
	size: DialogSize;
	/** If true controls overlaping dialogs borsers will not generate scrolls. (default: true) */
	noScroll: boolean;
	/**
	 * Property dialogs tabs definitions.
	 * This defines tabs that property dialog will contain.
	 * Property dialog will always have standard options tab.
	 */
	tabs: DialogTabDefinition[];
	/** Dialogs button list. Buttons that will be added to dialogs footer. (default: will have two default buttons "Cancel" and "Apply") */
	buttons: DialogButtonDefinition[];
	/**
	 * Callback that will be called when dialog is initialised.
	 * @param {FieldsIndex} fields object that contains controls referenced in this dialog.
	 */
	init: (fields: FieldsIndex) => void;
	/**
	 * Callback that will be called when dialog is openend.
	 * @param {FieldsIndex} fields object that contains controls referenced in this dialog.
	 */
	open: (fields: FieldsIndex) => void;
	/**
	 * Callback that will be called when dialog is closed.
	 * @param {FieldsIndex} fields object that contains controls referenced in this dialog.
	 */
	close: (fields: FieldsIndex) => void;
}

/** This defines property dialogs tab. */
interface DialogTabDefinition {
	/** Tab ordering priority. Lowest priority makes left most tab. (optional) */
	priority: number;
	/** Tab display name. (default: General) (optional) */
	name: string;
	/** If true will not be rendered or initialised. (optional) */
	ignore: boolean;
	/** (optional) */
	general: boolean;
	/** (optional) */
	options: boolean;
	/** Control list for this tab. (required) */
	children: ControlDefinition[];
}

/** This defines property dialogs buttons added to dialogs footer. */
interface DialogButtonDefinition {
	/** Display name for the button. (required) */
	name: string;
	/** Callback called when button is clicked. (default: will close dialog) (optional) */
	click: () => void;
	/** If true will make button different color. */
	primary: boolean;
}

/**
 * This defines control used in property dialog.
 * All other control definitions extend this control.
 * All control definitions are writen as plain JavaScript objects
 * (ex. {type: 'Label', text: 'Name', helpText: 'Name of something.'}).
 */
interface ControlDefinition {
	/** Control type, tells what kind of control this is. (required) */
	type: string;
	/** Reference id of the control, it will be used to reference this control in the code throuth FieldsIndex objects. (optional) */
	id: string;
	/** If true this control definition will be ignored and control will not be initiated. (optional) */
	ignore: boolean;
	/** Set initial visibility of control. (default: true) (optional) */
	visible: boolean;
	/** If true control will be enabled if false control will be disabled (inactive). (default: true) (optional) */
	enabled: boolean;
	/** Style class to add on root element of the control. (optional) */
	styleClass: string;
	/** Object defining css styles for controls root element. (optional) */
	css: Object;
	/** Object defining html attributes for controls root element. (optional) */
	attr: Object;
	/**
	 * Callback that will be called when control is initialised. (optional)
	 * @param {ControlDefinition} def definition object of this control.
	 * @param {FieldsIndex} fields object that contains controls referenced up to this point in this dialog.
	 */
	init: (def: ControlDefinition, fields: FieldsIndex) => void;
	/**
	 * Callback that will be called when control is clicked. (optional)
	 * @param {Event} e click event object.
	 * @param {FieldsIndex} fields object that contains controls referenced in this dialog.
	 */
	click: (e: Event, fields: FieldsIndex) => void;
}

/**
 * Object that contains controls referenced in this plugins property dialog.
 * Every property of this object is named by controls reference id.
 * All controls in this index object implements PropertyDialogControl interface.
 * @see PropertyDialogControl
 */
interface FieldsIndex {}

/**
 * This is control instance from FieldsIndex.
 */
interface PropertyDialogControl {
	/**
	 * Get control value.
	 */
	getValue(): any;
	/**
	 * Set control value.
	 */
	setValue(value: any): void;
}

/** Builder website body descriptor. */
interface PluginBody {
	
	/** Returns 'Body' */
	getType(): string;
	
	/** Change element selection to only contain these elements. */
	setElementSelection(elements: PluginElement[]): void;
}

/** Builder website content block descriptor. */
interface PluginContent {
	
	/** Returns 'Content' */
	getType(): string;
	
	/** Get horizontal offset relative to 'Body'. */
	getX(): number;
	
	/** Get vertical offset relative to 'Body'. */
	getY(): number;
	
	/** Get width of this block. */
	getWidth(): number;
	
	/** Get height of this block. */
	getHeight(): number;
	
	/** Get 'Body' descriptor. */
	getParent(): PluginBody;
	
	/** Add element to this content block. */
	addElement(element: PluginElement): void;
}

/** Builder website element descriptor. */
interface PluginElement {
	
	/** Returns plugin id that this element was created with. */
	getType(): string;
	
	/** Get unique identifier of this element in this page. */
	getId(): string;
	
	/** Get horizontal offset relative to 'Content' block. */
	getX(): number;
	
	/** Get vertical offset relative to 'Content' block. */
	getY(): number;
	
	/** Get width of this element. */
	getWidth(): number;
	
	/** Get height of this element. */
	getHeight(): number;
	
	/** Set horizontal offset relative to 'Content' block. */
	setX(x: number);
	
	/** Set vertical offset relative to 'Content' block. */
	setY(y: number);
	
	/** Get content block this element belongs to. */
	getParent(): PluginContent|null;
	
	/** Serialize element to data object for saving. */
	toData(): object;
}

/** Builder context menu descriptor. */
interface PluginContextMenu {
	/** Get identifier of this menu. */
	getId(): string;
	
	/** Set visibility of menu item representing this menu. */
	setVisible(visible: boolean): void;
	
	/** Add item to this menu. */
	addItem(id: string|null, name: string, func: () => void, icon?: string, index?: number): PluginContextMenuItem;
	
	/** Add item with sub-menu. */
	addMenu(id: string|null, name: string, icon?: string, index?: number): PluginContextMenu;
	
	/** Add menu item separator. */
	addSeparator(index?: number);
	
	/** Get item by ID from this menu. */
	getItem(id: string): PluginContextMenu|PluginContextMenuItem|null;
	
	/** Test if this menu has item identified by this id. */
	hasItemById(id: string): boolean;
	
	/** Remove item from this menu. */
	removeItem(item: PluginContextMenuItem|PluginContextMenu): void;
	
	/** Remove all item from this menu. */
	removeAllItems(): void;
}

/** Builder context menu item descriptor. */
interface PluginContextMenuItem {
	/** Get identifier of this item. */
	getId(): string;
	
	/** Get context menu this item belongs to. */
	getParent(): PluginContextMenu;
	
	/** Gate name of this item. */
	setName(name: string): void;
	
	/** Mark this item as selected. */
	setSelected(selected: boolean): void;
	
	/** Mark this item as enabled/disabled. */
	setEnabled(enabled: boolean): void;
	
	/** Set visibility of this item. */
	setVisible(visible: boolean): void;
}

/*
```

## Controls

```javascript
*/

/** This is custom container for creating custom controls. */
interface CustomContainerDefinition extends ControlDefinition {
	// type: string = 'CustomContainer';
	/** HTML tag to use for root element of this control. (default: div) (optional) */
	tag: string;
	/** HTML content of the control element. (optional) */
	content: string;
}

/** This is HTML form control. */
interface FormDefinition extends ControlDefinition {
	// type: string = 'Form';
	/** HTML Form action attribute. (optional) */
	action: string;
	/** HTML Form method attribute. (optional) */
	method: string;
	/** HTML Form enctype attribute. (optional) */
	enctype: string;
	/** Callback called on form submit. (optional) */
	submit: (fields: FieldsIndex) => void;
	/** Child control list for this control. (optional) */
	children: ControlDefinition[];
}

/**
 * This control is for arranging other controls in a responsive way.
 * This will arrange controls vertically.
 */
interface VerticalLayoutDefinition extends ControlDefinition {
	// type: string = 'VerticalLayout';
	/** Set CSS class to use for every row of this layout. (optional) */
	rowStyleClass: string;
	/** Spacing in pixels between controls in the layout. (default: 0) (optional) */
	spacing: number;
	/** Child control list for this control. (required) */
	children: ControlDefinition[];
}

/**
 * This control is for arranging other controls in a responsive way.
 * This will arrange controls hirizontaly (space for controls will be distributed evenly).
 */
interface HorizontalLayoutDefinition extends ControlDefinition {
	// type: string = 'HorizontalLayout';
	/**
	 * Weights for distributing space for controls in this layout.
	 * This is an array of integer numbers, sum of which must not exceed 12.
	 * Number of array items must match number of child controls.
	 * Higher number means more space is asigned for this control.
	 * (default: space is distributed evenly)
	 * (optional)
	 */
	columnWeights: number[];
	/**
	 * Weights for distributing space for controls on smaller screens in this layout.
	 * This is an array of integer numbers, sum of which must not exceed 12.
	 * Number of array items must match number of child controls.
	 * Higher number means more space is asigned for this control.
	 * (default: space is distributed evenly)
	 * (optional)
	 */
	columnWeightsSmall: number[];
	/** Child control list for this control. (required) */
	children: ControlDefinition[];
}

/**
 * This control is for arranging other controls in a responsive way.
 * This will arrange controls hirizontaly (aligned to left or right).
 */
interface FlowLayoutDefinition extends ControlDefinition {
	// type: string = 'FlowLayout';
	/** Spacing in pixels between controls in the layout. (default: 10) (optional) */
	spacing: number;
	/** Horizontal alignment of controls ('left' or 'right'). (default: left) (optional) */
	align: string;
	/**
	 * Vertical alignment of controls (if they are in different height) ('top', 'middle' or 'bottom').
	 * (default: top)
	 * (optional)
	 */
	verticalAlign: string;
	/** Child control list for this control. (required) */
	children: ControlDefinition[];
}

/**
 * This creates tooltip icon, that will show tooltip text on mouse over.
 */
interface TooltipDefinition extends ControlDefinition {
	// type: string = 'Tooltip';
	/** Tooltip icon text. (default: '?') (optional) */
	label: string;
	/** Text that will be shown when mouse over on tooltip icon. (required) */
	text: string;
	/** Tooltip placement around tooltip icon ('top', 'right', 'bottom' or 'left'). (default: right) (optional) */
	placement: string;
}

/** This creates HTML image control. */
interface ImageDefinition extends ControlDefinition {
	// type: string = 'Image';
	/** HTML image src attribute. (required) */
	src: string;
}

/** This creates button control. */
interface ButtonDefinition extends ControlDefinition {
	// type: string = 'Button';
	/** Buttons display text. (required) */
	text: string;
	/** Buttons icon (css style class, use bootstraps 'glyphicon glyphicon-*' classes). (optional) */
	icon: string;
	/** Buttons visual style. ('success', 'info', 'danger', 'warning', 'primary', 'default' or 'link)
	 * (default: default)
	 * (optional)
	 */
	buttonStyle: string;
	/** Button type ('button' or 'submit'). (default: button) (optional) */
	buttonType: string;
}

/** This creates label control. */
interface LabelDefinition extends ControlDefinition {
	// type: string = 'Label';
	/** Labels display text. (required, optional if html is set) */
	text: string;
	/** Labels display text as HTML. (optional, required if text is not set) */
	html: string;
	/** Adds tooltip icon, that will show this tooltip text. (optional) */
	helpText: string;
	/**
	 * Tooltip placement direction when tooltip is shown ('top', 'right', 'bottom' or  'left').
	 * (default: right)
	 * (optional)
	 */
	helpPlacement: string;
}

/** This creates text field control. */
interface TextFieldDefinition extends ControlDefinition {
	// type: string = 'TextField';
	/** Placeholder text for this text field. (optional) */
	placeholder: string;
	/** Enable multiline text input. (default: false) (optional) */
	textArea: boolean;
	/** If true and multiline text input enabled this control will be resizable by user. (default: false) (optional) */
	resizable: boolean;
	/** Initial value of this control. (optional) */
	value: string;
	/** If true and not in multiline input mode, it will make this control into password input field. (default: false) (optional) */
	password: boolean;
	/** HTML title attribute of this control. (optional) */
	title: string;
	/** Callback that is called when content of this field is changed. (optional) */
	change: (fields: FieldsIndex) => void;
}

/** This creates text field control with multilingual input. */
interface MultilangTextFieldDefinition extends ControlDefinition {
	// type: string = 'MultilangTextField';
	/** Enable multiline text input. (default: false) (optional) */
	textArea: boolean;
	/** If true and multiline text input enabled this control will be resizable by user. (default: false) (optional) */
	resizable: boolean;
	/** Initial value of this control. (optional) */
	value: string|Object;
}

/** This creates HTML file input element control. */
interface FileFieldDefinition extends ControlDefinition {
	// type: string = 'FileField';
	/** HTML name attribute. (optional) */
	name: string;
}

/** This will create radio-button control. */
interface RadioBoxDefinition extends ControlDefinition {
	// type: string = 'RadioBox';
	/** Radio button label. (optional) */
	label: string;
	/** Group name for grouping multiple radio button controls. (required) */
	group: string;
	/** (optional) */
	inline: boolean;
	/** HTML value attribute for this control. (optional) */
	inputValue: string;
	/** If true radio button will be selected else unselected. (default: false) (optional) */
	value: boolean;
	/** HTML title attribute of this control. (optional) */
	title: string;
	/** Adds tooltip icon, that will show this tooltip text. (optional) */
	helpText: string;
	/** Callback that is called when radio button is selected/unselected. (optional) */
	change: (fields: FieldsIndex) => void;
}

/** This will create check-box control. */
interface CheckBoxDefinition extends ControlDefinition {
	// type: string = 'CheckBox';
	/** Check box label. (optional) */
	label: string;
	/** (optional) */
	inline: boolean;
	/** If true check box will be checked else unchecked. (default: false) (optional) */
	value: boolean;
	/** Adds tooltip icon, that will show this tooltip text. (optional) */
	helpText: string;
	/** Callback that is called when check box is checked/unchecked. (optional) */
	change: (fields: FieldsIndex) => void;
}

/** This will create dropdown control. */
interface DropdownBoxDefinition extends ControlDefinition {
	// type: string = 'DropdownBox';
	/** List of options for dropdown box. (required) */
	options: DropdownBoxOptionDefinition[];
	/** Option id to select in dropdown box. (optional) */
	value: string;
	/** Callback that will be called when option is selected. (optional) */
	change: (fields: FieldsIndex) => void;
}

/** This will create option for dropdown box. */
interface DropdownBoxOptionDefinition {
	// type: string = 'DropdownBoxOption';
	/** Option unique identifier (needs to unique in this dropdown). (required) */
	id: string;
	/** Display name of this option. (required, optional if html property is set) */
	name: string;
	/** Display name as HTML of this option. (optional, required if name property is not set) */
	html: string;
}

/**
 * This will create menu item selector.
 * It will allow selecting menu item of any currently available menu element.
 */
interface MenuItemSelectorDefinition extends ControlDefinition {
	// type: string = 'MenuItemSelector';
	/** Menu item id to select. (optional) */
	value: number;
}

/** This will create link edit control. */
interface LinkEditSelectorDefinition extends ControlDefinition {
	// type: string = 'LinkEditSelector';
	/** Initial value for this control. (optional) */
	value: string;
	/** Message when no value is set. (required) */
	emptyMsg: string;
}

/** This will create color picker. */
interface ColorSelectorDefinition extends ControlDefinition {
	// type: string = 'ColorSelector';
	/** If true will not allow to select transparent color. (default: false) (optional) */
	noTransparent: boolean;
	/** if true will render color picker control as big thumbnail. (default: false) (optional) */
	big: boolean;
	/** If true control width will ajust to parent width else will be fixed width. (default: false) (optional) */
	noFidexWidth: boolean;
	/** Initial color in a hex form (ex. #000000). (optional) */
	value: string;
	/** Callback that will be called when color is changed. (optional) */
	change: (fields: FieldsIndex) => void;
}

/** This will create number only input field with increase/decrease buttons. */
interface SizeSelectorDefinition extends ControlDefinition {
	// type: string = 'SizeSelector';
	/** Minimum allowed value. (default: 1) (optional) */
	min: number;
	/** Maximum allowed value. (default: 9999) (optional) */
	max: number;
	/** Value by what controls value will change when increase/decrease buttons are pressed. (default: 1) (optional) */
	step: number;
	/** If true then when value is at max and increase button is pressed value will change to min instead of stopping. (default: false) (optional) */
	repeat: boolean;
	/** If true control width will ajust to parent width else will be fixed width. (default: false) (optional) */
	noFixedWidth: boolean;
	/** Initial control value. (optional) */
	value: number;
	/** Units name to be rendered on the right of the field (ex. 'px' for pixels). (optional) */
	units: string;
	/** Callback that will be called when value changed. (optional) */
	change: (fields: FieldsIndex) => void;
}

/**
 * This will create media picker.
 * It will allow to select one or more files from gallery.
 */
interface ImageSelectorDefinition extends ControlDefinition {
	// type: string = 'ImageSelector';
	/** Control mode ('all', 'image', 'sound' or 'flash') defines what kind of files can be selected. (required) */
	mode: string;
	/** Size (in pixels) of the controls freview thumbnail. (default: 60) (optional) */
	thumbSize: number;
	/** If true will allow to select multiple files. (default: false) (optional) */
	multiselect: boolean;
	/** If true will enable additional options to select external images. (default: true) (optional) */
	mediatabs: boolean;
	/** Initially selected files. (optional) */
	value: string[];
	/** Callback that will be called when selected files changes. (optional) */
	select: (fields: FieldsIndex) => void;
}

/**
 * This will create link selector control.
 * It will allow user to enter URL manually, choose one of the pages from site or choose file from gallery.
 */
interface LinkSelectorDefinition extends ControlDefinition {
	// type: string = 'LinkSelector';
	/** Enable multilingual mode. (default: false) (optional) */
	multilang: boolean;
	/** Initial value. (optional) */
	value: Object;
}

/** This will create control to select one of text styles from builder styles dialog. */
interface StyleSelectorDefinition extends ControlDefinition {
	// type: string = 'StyleSelector';
	/** If true will add option to choose none. (default: false) (optional) */
	emptyChoice: boolean;
	/** Initially selected style id. (optional) */
	value: string;
}

/** This will create font selector. */
interface FontSelectorDefinition extends ControlDefinition {
	// type: string = 'FontSelector';
	/** If true allows to select line height. (default: false) (optional) */
	needLineHeight: boolean;
	/** Informational tooltip placement in control. (default: left) (optional) */
	ttPlacement: string;
	/** If true will add font preview box. (default: false) (optional) */
	needPreview: boolean;
	/** If false will allow to select font style. (default: false) (optional) */
	noStyle: boolean;
	/** If false will allow to select font alignment. (default: false) (optional) */
	noAlign: boolean;
	/** If true control layout will be more horizontal. (default: false) (optional) */
	horizontal: boolean;
	/** Initial value. (optional) */
	value: Object;
}

/** This will create font family selector control. */
interface FontFamilySelectorDefinition extends ControlDefinition {
	// type: string = 'FontFamilySelector';
	/** If true control width will ajust to parent width else will be fixed width. (default: false) (optional) */
	noFixedWidth: boolean;
	/** Initial value. (optional) */
	value: string;
	/** Callback that will be called when selection changes. (optional) */
	change: (fields: FieldsIndex) => void;
}

/** This will create font style selector control. */
interface FontStyleSelectorDefinition extends ControlDefinition {
	// type: string = 'FontStyleSelector';
	/** Initial value. (optional) */
	value: Object;
	/** Callback that will be called when selection changes. (optional) */
	change: (fields: FieldsIndex) => void;
}

/** This will create font transform selector control. */
interface FontTransformSelectorDefinition extends ControlDefinition {
	// type: string = 'FontTransformSelector';
	/** Initial value. (optional) */
	value: string|Object;
}

/** This will create spacing selector control. */
interface SpacingSelectorDefinition extends ControlDefinition {
	// type: string = 'SpacingSelector';
	/** Initial value. (optional) */
	value: Object;
}

/** This will create horizontal alignment selector control. */
interface HAlignSelectorDefinition extends ControlDefinition {
	// type: string = 'HAlignSelector';
	/** Initial value. (optional) */
	value: string;
	/** Callback that will be called when selection changes. (optional) */
	change: (fields: FieldsIndex) => void;
}

/** This will create border selector control. */
interface BorderSelectorDefinition extends ControlDefinition {
	// type: string = 'BorderSelector';
	/** Placement of border editor popup ('top', 'right', 'bottom' or 'left'). (default: bottom) (optional) */
	placement: string;
	/** If true will allow to select border radius. (default: false) (optional) */
	showRadius: boolean;
	/** If false will allow to select transparent border color. (default: true) (optional) */
	noTransparent: boolean;
	/** Initial value. (optional) */
	value: Object;
}

/** This will create horizontal and vertical alignment selector control. */
interface AlignSelectorDefinition extends ControlDefinition {
	// type: string = 'AlignSelector';
	/** Initial value. (optional) */
	value: string;
	/** Callback that will be called when selection changes. (optional) */
	change: (fields: FieldsIndex) => void;
}

/** This will create background (image, image-position, image-repeat, color) selector control. */
interface BackgroundSelectorDefinition extends ControlDefinition {
	// type: string = 'BackgroundSelector';
	/** Initial value. (optional) */
	value: Object;
	/** Callback that will be called when selection changes. (optional) */
	change: (fields: FieldsIndex) => void;
}

/** This will create corner roundness selector control. */
interface RoundCornersSelectorDefinition extends ControlDefinition {
	// type: string = 'RoundCornersSelector';
	/** Initial value. (optional) */
	value: Object;
}

/** This will create layout blocks selector control. */
interface LayoutBlocksControlDefinition extends ControlDefinition {
	// type: string = 'LayoutBlocksControl';
	/** Initial value. (optional) */
	value: string;
	/** Callback that will be called when selection changes. (optional) */
	change: (fields: FieldsIndex) => void;
}

/** This will create editable list-box control. */
interface ListDefinition extends ControlDefinition {
	// type: string = 'List';
	/** Item class. (required) */
	itemType: typeof Object,
	/** If true will be editable. (default: false) (optional) */
	editable: boolean;
	/** List box height in pixels. (optional) */
	listHeight: number;
	/** Callback that will be called when list item is selected. (optional) */
	select: (fields: FieldsIndex, item: Object) => void;
	/** Callback that will be called when add item link is clicked. (optional) */
	add: (fields: FieldsIndex, item: Object) => void;
}

/** This will create HTML form element control. */
interface IFrameDefinition extends ControlDefinition {
	// type: string = 'IFrame';
	/** HTML src attribute (iframe URL). (default: 'about:blank') (optional) */
	url: string;
	/** Query params that will be added to url. (optional) */
	params: Object;
}

/** This will create informational box control. */
interface AlertBoxDefinition extends ControlDefinition {
	// type: string = 'AlertBox';
	/** Information message to be displayed. (required) */
	text: string;
	/** Alert box style ('success', 'info', 'danger', 'warning', 'primary' or 'default'). (default: default) (optional) */
	style: string;
}

/** This will create preloader overlay control. */
interface PreloaderOverlayDefinition extends ControlDefinition {
	// type: string = 'PreloaderOverlay';
	/** Initial value. (optional) */
	value: number;
}

/** This will create contact form configurator control. */
interface ContactFormDefinition extends ControlDefinition {
	// type: string = 'ContactForm';
}

/** This will create tabbed pane control. */
interface TabbedPaneDefinition extends ControlDefinition {
	// type: string = 'TabbedPane';
	/** Tabbed pane height in pixels. (optional) */
	fixedHeight: number;
	/** If true scroll will be hidden. (default: false) (optional) */
	noScroll: boolean;
	/** Dialog tabs. (required) */
	tabs: DialogTabDefinition[];
	/** Callback that will be called when tab is selected. (optional) */
	change: (fields: FieldsIndex, tab: Object, prevTab: Object) => void;
}

/*
```

*/
Some examples
  • Using List object (zip)
  • Using custom object list (zip)

Plugin testing (only for enterprise)

If you are using enterprise plan (Enterprise license on licenses page) then you are given one more option of testing the plugin.
Instead of using built-in system you can work on plugin in your builder at once.
All plugins are stored in builder folder "plugins" and are distributed by folders.
To add your plugin in builder you need to take the following steps:

  1. create folder "myplugin" inside builder plugins folder
  2. create files "main.html", "main.js" and "main.png" in folder "plugins/myplugin" (please see documentation above)
  3. add plugin logic to specified files (please see documentation above)
  4. create file "main.json" in folder "myplugin" and add content {"admin": true}
  5. register your plugin in builder by adding it to file "plugins/plugins-ext.json" (create it if it does not exist; this file should share the same structure with file "plugins/plugins.json")

After adding plugin you can test it in admin builder mode ("Create custom templates" link in your Enterprise license).

To make plugin available for all your customers set parameter {"admin": false} in file "main.json".

Using PHP code in plugin (only for enterprise)

Sometimes it is not enough to use HTML/Javascript code for creating plugin. Often 3rd party plugins provide REST API for integration or manipulation of the system, for example:

  • shops require saving orders
  • plugins may use PHP libraries
  • plugins may require encrypting form fields before submitting what requires server-side script like PHP

To add PHP code into your plugin take the following steps:

  1. create file "main.php" in your plugin folder and write your PHP code
  2. open file "main.html" and add marker {{{requireService}}}. The marker will be replaced with PHP code in published website
  3. open file "plugins/plugins_whitelist.json" and add your plugin name to php list
Note.
PHP code is not loaded in site builder but only in published website.
Adding multiple PHP files to plugin

Let's say you develop plugin which integrates some 3rd party system to your site. This system provides PHP SDK which you want to use.
The PHP SDK can be a folder which contains one or multiple .php files, other folders containing other files, etc.
To add all this PHP library to your plugin take the following steps:

  1. create folder "site" in your plugin folder
  2. add all needed 3rd party files/folders into folder "site"

After that you will have access to folder "site" within your PHP file "main.php".
Let's presume you have file in your plugin "myplugin/site/SomeLibrary.php". Then you can load this file with line require_once("SomeLibrary.php"); in your PHP file.

E-commerce plugin (only for enterprise)

In this section e-commerce plugin is considered as a payment gateway which can be added to "Store Cart" widget in builder.
Let's say you need to have payment gateway in your Store which does not currently exist in site builder. Then creation of e-commerce plugin is a right choice.

Simple payment plugin

The creation of e-commerce plugin can be started from creation of a simple payment plugin. Let's name simple payment plugin a button for which website owner can set some predefined product(s), amount, price, etc. (similar to Buy Now button of Paypal system). To deal with it you need to use documentation (API) of payment gateway system which you want to integrate — you will need to know what parameters and what endpoint you need to submit the form to.

Let's write the code for a simple payment plugin for a dummy payment gateway:

main.js:

PluginWrapper.registerPlugin('myplugin', {
    name: 'myplugin',
    element: {
        minSize: {width: 50, height: 50},
        defaultSize: {width: 100, height: 100},
        resizable: true
    },
    propertyDialog: {
        tabs: [
            {children: [
                {type: 'VerticalLayout', spacing: 15, children: [
                    {type: 'HorizontalLayout', columnWeights: [6, 6], css: {marginTop: 15}, children: [
                        {type: 'VerticalLayout', children: [
                            {type: 'Label', text: 'Merchant ID', helpText: 'Your identifier in payment system'},
                            {type: 'TextField', id: 'merchant'}
                        ]},
                        {type: 'VerticalLayout', children: [
                            {type: 'Label', text: 'Password', helpText: 'Your password provided by payment system'},
                            {type: 'TextField', id: 'password'}
                        ]}
                    ]},
                    {type: 'HorizontalLayout', columnWeights: [4, 4, 4], css: {marginTop: 15}, children: [
                        {type: 'VerticalLayout', children: [
                            {type: 'Label', text: 'Product Name'},
                            {type: 'TextField', id: 'name'}
                        ]},
                        {type: 'VerticalLayout', children: [
                            {type: 'Label', text: 'Price'},
                            {type: 'TextField', id: 'price'}
                        ]},
                        {type: 'VerticalLayout', children: [
                            {type: 'Label', text: 'Currency'},
                            {type: 'DropdownBox', id: 'currency', options: [
                                {id: '#USD', name: 'USD', value: 'USD'},
                                {id: '#EUR', name: 'EUR', value: 'EUR'},
                                {id: '#GBP', name: 'GBP', value: 'GBP'}
                            ]}
                        ]}
                    ]}
                ]}
            ]}
        ]
    },
    resizeAction: function (data, elem) {
        if (!this.resizeTimeout) {
            var self = this;
            this.resizeTimeout = setTimeout(function () {
                self.resizeTimeout = null;
                self.updateElement();
            }, 1000);
        }
    },
    openAction: function(fields, data, elem) {
        var itm;
        itm = fields.currency.getItemById('#' + data.content.currency);
        fields.currency.selectItem(itm);
        fields.merchant.setText(data.content.merchant);
        fields.password.setText(data.content.password);
        fields.name.setText(data.content.name);
        fields.price.setText(data.content.price);
    },
    applyAction: function(fields, data, elem) {
        var itm;
        itm = fields.currency.getSelectedItem();
        data.content.currency = itm.getOriginal().value;
        data.content.merchant = fields.merchant.getText();
        data.content.password = fields.password.getText();
        data.content.name = fields.name.getText();
        data.content.price = fields.price.getText();
    },
    // set some default values
    loadAction: function (data) {
        if (!data.content.name)
            data.content.name = 'Default product name';
        if (!data.content.price)
            data.content.amount = '10';
        if (!data.content.currency)
            data.content.currency = 'USD';
    }
});
	

main.html:

<form action="[payment gateway endpoint]" method="post" target="_blank">
    <input type="hidden" name="d_merchantid" value="{{content.merchant}}" />
    <input type="hidden" name="d_productname" value="{{content.name}}" />
    <input type="hidden" name="d_productprice" value="{{content.price}}" />
    <input type="hidden" name="d_currency" value="{{content.currency}}" />
    {{{requireService}}}
    <button type="submit">Pay</button>
</form>
	

main.php:

<?php

$password = md5($pluginData->password);
echo '<input type="hidden" name="d_password" value="'.$password.'" />';
echo '<input type="hidden" name="d_transactionid" value="'.md5(microtime()).'" />';
	
We added "main.php" file to make payment gateway look more realistic — so that it requires submitting encrypted password with the form rather than plain password value.
Usually payment systems require to encrypt set of some form field values and add it to that form as an extra field.
On website the {{{requireService}}} string will be replaced with an output that PHP code generates.

The plugin does not do much — it creates form which submits order information to payment system by clicking button "Pay". The payment system handles sent data and offers payment methods to a client.

The plugin properties dialog should look like this in builder interface:
Simple payment plugin properties dialog for dummy payment gateway

Payment gateway plugin for Store Cart widget

To integrate your plugin to store some changes must be made to a simple payment plugin:

  • modifying file "main.js"
  • modifying files "main.html"
  • adding PHP file with payment gateway class description
Modifying file "main.js"

The site builder should know that your plugin is a payment gateway and it has to be included to Store Cart widget as an option.
To let builder know about it we add the following code at the very beginning of "main.js" file:


ElementRegister.registerPaymentGateway({
    name: "Dummy system",               // (required) name of payment gateway inside Store Cart widget
    id: "myplugin",                     // (required) must be the same as your plugin name
    priceFieldId: "price",              // (required) plugin price field ID in your plugin defined in property "propertyDialog"
    keyFieldId: "merchant",             // (required) field ID in your plugin defined in property "propertyDialog"
    keyField2Id: "password",            // (optional) another field ID in your plugin defined in property "propertyDialog"
    keyFieldDef: {type: "HorizontalLayout", columnWeights: [6,6], noPadding: true, children: [
        {type: "TextField", placeholder: "Merchant ID", id: "key"},
        {type: "TextField", placeholder: "Password", id: "key2"}
    ]},
    nameFieldId: "name",                // (optional) plugin product name field ID in your plugin defined in property "propertyDialog"
    globalVars: ["merchant","password"] // (optional) these variables will be available in PHP code
});
	

Let's consider these parameters in more detail:

  • name — Name of payment gateway system inside Store Cart widget. This property is only informative and does not affect any plugin logic.
  • id — This property tells builder what plugin payment gateway will be associated with. The value for this property must be the same as your plugin name.
  • priceFieldId — Just specify field ID which stands for price in your property "propertyDialog" in "main.js". From the beginning the price is not known in Store and therefore it is replaced with a special placeholder {{price}} in HTML form. When you submit the form this placeholder will be replaced with a real order price and sent to payment system.
  • keyFieldId — Specify merchant field ID here (or any other parameter which is related to payment system merchant identifier).
  • keyField2Id — Here we presume that our Dummy payment gateway specifies password which must be also used in the form (for example in signature calculation). So we add it as a second parameter.
    Note: You can use any number of parameters of such format: keyField3Id, keyField4Id, etc.
  • keyFieldDef — This field accepts object which describes layout of properties specified above (keyFieldId, keyField2Id) in Store Cart properties dialog. The structure of this object is the same as of "propertyDialog". Developing your payment gateway you can see examples of this property used in other predefined payment gateways (e.g. in "plugins/assist/main.js").
  • nameFieldId — Just specify field ID which stands for product name/description in your property "propertyDialog" in "main.js".
  • globalVars — Specify array containing field IDs which you will need to have in PHP code. In example above we specified fields "merchant" and "password". We presume that after successful payment the payment system will send IPN (Instant payment notification) to website which must be handled properly. The information which payment system sends in this step can be encrypted and to decrypt it you might use your merchant ID or password values.
    Note: Of course this depends on payment system. You must read documentation which payment system provides and be able to handle payment result properly. If payment system for which you implement plugin does not use IPN or it does not require calculating signature then you can omit property "globalVars".

After adding this part of code you should see something like this in Store Cart widget properties dialog:
Store Cart widget properties dialog

Modifying file "main.html"

When you integrate your plugin in Store widget then a special variable {{content.store}} is available inside "main.html" file.
Let's see how the file should look like now:


<form action="[payment gateway endpoint]" method="post"
{{^content.store}} target="_blank"{{/content.store}}
{{#content.store}} data-gateway-id="DummySystem"{/content.store}}>
    <input type="hidden" name="d_key" value="{{content.key}}" />
    <input type="hidden" name="d_productname" value="{{content.name}}" />
    <input type="hidden" name="d_productprice" value="{{content.price}}" />
    <input type="hidden" name="d_currency" value="{{content.currency}}" />
    {{#content.store}}
    <input type="hidden" name="d_transactionid" value="{transactionId}" />
    <input type="hidden" name="d_callbackurl" value="{callbackUrl}" />
    <input type="hidden" name="d_returnurl" value="{returnUrl}" />
    <input type="hidden" name="d_cancelurl" value="{cancelUrl}" />
    <input type="hidden" name="d_verifyurl" value="{verifyUrl}" />
    {{/content.store}}
    {{{requireService}}}
    <button type="submit">Pay</button>
</form>
	

The changes we made are:

  • Made attribute target="_blank" not used in form when the form is used in Store widget.
  • Added attribute data-gateway-id (this is required for Store widget).
  • Added form parameter {transactionId}. This is a placeholder which will be replaced with a real transaction ID generated by Store widget when submitting the form.
  • Added form parameters {callbackUrl}, {returnUrl}, {cancelUrl} and {verifyUrl}. These are placeholders which will be replaced by real URLs generated by Store widget when page is loaded. Let's consider these parameters in more detail:
    • {callbackUrl} — Add this placeholder if your payment system supports IPN. The placeholder will be replaced with URL which must be used by payment system as IPN URL.
      Note: Some payment systems do not support passing URL as a form parameter. Intead they let you pre-configure this URL in merchant account. In such case just do not add this parameter to the form and see how this issue is solved in predefined payment gateway plugin "Robokassa" (file "plugins/robokassa/main.js").
    • {returnUrl} — Add this placeholder if your payment system supports return URL. Usually payment systems let you specify URL where buyer will be redirected after successful payment.
    • {cancelUrl} — Add this placeholder if your payment system supports cancel URL. Usually payment systems let you specify URL where buyer will be redirected after canceling payment (or after payment was unsuccessful).
    • {verifyUrl} — Add this placeholder if your payment system supports payment verifying URL. Sometimes payment systems let you specify URL which will be called by payment system before callback URL in order to check if payment is not fake and really exists. Usually in this case payment systems require to output some message indicating of payment existence.

We can notice that parameter d_transactionid is added twice to the form when using payment system from Store widget — first time in "main.html" file and the second time in "main.php" file. To fix this problem we need to edit "main.php" file:


<?php

$password = md5($pluginData->password);
echo '<input type="hidden" name="d_password" value="'.$password.'" />';
if (!$pluginData->store) {
	echo '<input type="hidden" name="d_transactionid" value="'.md5(microtime()).'" />';
}
	

Also we can notice that parameter d_password is not added to the form at all. This parameter will be added to the form by PHP gateway payment class.

Adding PHP gateway payment class

In previous sections we managed to submit form to payment system when the form is used in Store widget. Now the form can send dynamic price taking it from Store and pass extra parameters like IPN URL needed for payment system to inform website about payment status. After that we need write logic which will handle IPN call on website and send appropriate response (if needed).

1. Create file "plugins/myplugin/site/GatewayMyplugin.php".
Note: The name of the file should be constructed from concatenated words "Gateway" and your plugin name with capitalized first letter (e.g. if plugin name is "abc" then gateway class must be "GatewayAbc" or if plugin name is "abc_def" then gateway class must be "GatewayAbcDef").

2. Add the following contents to the file:


<?php

class GatewayMyplugin extends PaymentGateway {

    /** @var boolean */
    protected $returnAfterCallback = false;

    public function init() {}

    /** @return string */
    public function getTransactionId() {}

    /**
     * @param array $formVars
     * @return string[]
     */
    public function createFormFields($formVars) {}

    /**
     * @param array $formVars
     * @return string
     */
    public function createRedirectUrl($formVars) {}

    /**
     * @param array $formVars
     * @return string
     */
    public function createRedirectUrl($formVars) {}

    /**
     * @param StoreModuleOrder $order
     * @return boolean
     */
    public function callback(StoreModuleOrder $order = null) {
        return true;
    }

    /** @param StoreModuleOrder $order */
    public function verify(StoreModuleOrder $order = null) {}

}
	

The class must extend abstract class "PaymentGateway" which contains one required method "getTransactionId". All other methods are optional and should be used only if needed by payment system you are developing payment gateway plugin for.

We left all methods empty because the logic will depend on payment system requirements. Let's consider these methods in more details:

  • init — There can be any code for class initialisation. The method is called after class contructor.
    Note: This method must return boolean value. If it returns true then the payment will be set as successful. Otherwise the payment will be considered failed.
  • returnAfterCallback — Property indicating whether website must redirect client to Return URL right after Callback URL (IPN) was called and callback logic was processed.
    Note: Some payment systems prefer using separately return URL and callback URL. The client is redirected from payment system by return URL back to website (payment system → browser) and the callback URL is called later (payment system → server). In this case property "returnAfterCallback" must be false. Some payment systems prefer redirecting client back to website and pass payment status details at once with no separate IPN call. In this case property "returnAfterCallback" must be set to true.
  • getTransactionId — Must return value from request (POST or GET depending on payment system) of property which stands for transaction ID passed as parameter "d_transactionid" in form. This method is important because Store on website must identify payment by returned value. If Store does not find payment by transaction ID returned by this method it will not be able to set appropriate status to payment (complete,pending,failed).
  • createFormFields — Returns array of HTML inputs which should be embedded to the form right after a buyer submitted the form but the form has not been sent to payment system yet. The method accepts argument $formVars which is associative array containing serialized form information.
    Note 1: Some payment systems require calculating signature of values which are submitted. This can be done in this method and an extra form input returned.
    Note 2: If one of returned HTML inputs name matches name of already existing input in the form then that input will be completely replaced with returned in this method.
    Note 3: If you return boolean false then form will not be submitted.
  • createRedirectUrl — Returns URL which will be set to form "action" attribute. Some payment systems do not have static payment submiting URL. Intead they provide per merchant-basis URL or dynamic URL depending on form field values.
  • callback — The logic of callback. For example payment system might require to respond with some message which would indicate of successful IPN handling. In this case You can output any message like echo "OK"; (the message can be any depending on payment system). Note that you can also set your headers if payment system require (e.g. header('Content-Type: application/json; charset=utf-8');.
    Note: This method must return boolean value. If it returns true then the payment will be set as successful in Store. Otherwise the payment will be considered failed.
  • verify — The logic of payment verification. For example payment system might require to respond with some message which would indicate of payment existance. Otherwise payment system will not process the payment considering it fake. You can output any message like echo "OK"; (the message can be any depending on payment system).
In the class scope you can access property $this->config in any method. This is a property of type "stdClass" contains information stored in file "main.js" in property globalVars. For example in our Dummy payment gateway we can access our stored properties like this: $this->config->merchant and $this->config->password. Except our stored properties the object $config can also have these by default:
  • wbLang — 2-letter language code the website is currently on (set only if "Languages" widget is added in website).
  • wbDefLang — 2-letter language code of default site language (set only if "Languages" widget is added in website).
  • wbBaseLang — 2-letter language code of builder language the client was on while building website (always set).