Book a call

Fill out the form and we will call you back as soon as possible

Emergency situation

In case of emergencies or breakdowns, you can send an SMS to our emergency hotline

On-call phone (SMS only)

+45 29 70 15 95

Send an SMS with the following information:

  • Your name and webshop
  • Description of the problem
  • Your callback phone number

Notes: This service is only for critical situations where your webshop is down or has serious problems. For regular support, please use our normal support channels.

Shoporama Templates

Below is information as to how you develop Shoporama themes

Overview

In general, a Shoporama theme is a series of HTML files located in a folder on our ftp server. The template language in the individual files is Smarty, as mentioned below.

In the individual templates, there is access to a range of objects and methods. You can read more about them in our Template API.

Contact us at support@shoporama.dk if you have any questions.

Development environment

To make the development of Shoporama themes easier and faster, we have developed Hackorama - a local development environment that simulates Shoporama's production environment.

Hackorama makes it possible to develop and test your themes locally without having to deploy to production. This gives you faster feedback and a more efficient development process.

Functions

  • Local API data retrieval with caching system
  • Smarty 4 template engine with the same delimiters as Shoporama
  • Wrapper classes that mimic Shoporama's "Safe*" classes
  • Automatic image scaling and caching
  • Simulated customer login and checkout flow
  • Debug tools and logging

Get started

Follow these steps to get started with Hackorama:

# 1. Klon repository
git clone https://github.com/blinksbjerg/hackorama.git
cd hackorama

# 2. Konfigurer din API-nøgle i setup.php
# Rediger setup.php og indsæt din Shoporama API-nøgle

# 3. Start lokal PHP server
php -S localhost:8080

# 4. Åbn din browser
# Naviger til http://localhost:8080

Example: Testing product display

Here's an example of how you can test your product display in Hackorama:

# I din template fil (f.eks. product.html)
<div class="product-detail">
    <h1><{$product->getName()|escape}></h1>
    <div class="price">
        <{$product->getPrice()|currency}>
    </div>
    <div class="description">
        <{$product->getDescription()}>
    </div>
</div>

# Test i Hackorama ved at navigere til:
# http://localhost:8080/product?id=123

Tip: Hackorama includes the Alaska2 theme as an example. You can use this as a starting point for your own theme or as a reference for best practices.

Note: Hackorama is not a 1:1 copy of Shoporama's production environment. It supports the basic features needed for local theme development, but not all advanced features are implemented. This is designed to give you a fast and efficient development environment for the most common use cases.

Read more about Hackorama and see the full documentation on GitHub.

Smarty

The engine of our templating system is based on Smarty.

We have version 2.6 installed for backwards compatibility reasons, but we no longer maintain it and we recommend that everyone uses version 4.x. The version is set under settings for the shop.

Please note that this documentation only applies to version 4.x.

The difference between standard Smarty and ours is that we use <{ and }> as delimiters. I.e. the syntax is:

<h1>Velkommen til <{$webshop->getName()|escape}></h1>

Tips

Check content

If you are unsure what a variable is or what a function returns, you can run it through var_dump. If you have an object, you will always be able to see what methods are available in our Template API.

You use var_dump in the following way:

<{$order|var_dump}>
Check objects

In some cases, it is relevant to make sure that if you use a variable as an object, it is also an object. Otherwise the template will not work. We set some global variables such as $product, and$webshop and others. For those variables it is not necessary. They can be checked in one of the following ways:

<{* Typisk tilstrækkelig *}>
<{if $profile}>
	<{$profile->getName()}>
<{/if}>

<{* Lidt grundigere *}>
<{if is_object($profile)}>
	<{$profile->getName()}>
<{/if}>

<{* Meget grundig *}>
<{if is_a($profile, "SafeProfile")}>
	<{$profile->getName()}>
<{/if}>
Fejlfinding

If you want to see which variables are available on the page you are developing, you can activate Smarty's debug mode and view the variables in a new window. Insert the following code into your template:

<{debug}>

Dive deeper into Smarty4 here: Smarty 4 Documentation.

Available features and modifiers

In Shoporama, you have access to a selected set of Smarty modifiers and features that are approved for security reasons:

Modifiers
String manipulation
|strip_tags - Fjerner HTML tags
|substr:start:length - Udtrækker del af streng
|trim - Fjerner whitespace
|ucfirst - Stort begyndelsesbogstav
Arrays og tal
|implode:"," - Samler array til streng
|explode:"," - Deler streng til array
|count - Tæller elementer
|floor - Runder ned
|strlen - Længde af streng
Dato og andet
|strtotime - Konverterer dato til timestamp
|md5 - MD5 hash
Funktioner i conditionals
Validering
isset($var) - Tjekker om variabel er sat
is_array($var) - Tjekker om det er et array
is_object($var) - Tjekker om det er et objekt
intval($var) - Konverterer til heltal
Arrays og templates
in_array($needle, $haystack) - Søger i array
template_exists('file.html') - Tjekker om template findes
Eksempel:
<{if in_array($product->getProductId(), $featured_ids) && isset($show_badge)}>
    Featured
<{/if}>

File structure

You get ftp access to your shop and in the root of your directory, put your theme. The name of the folder is the name of the theme.

/{navn}
Main folder for your theme
/{navn}/components
Folder with components for Page Builder
/{navn}/components/sections.json
List of which sections can be inserted on which pages
/{navn}/components/tags.json
Tags to be used in theme
/{navn}/components/sections
Folder for the individual section files
/{navn}/extensions
Extensions to Shoporama's built-in data types.
/{navn}/templates
Folder to templates
/{navn}/templates/mails
Folder for email templates

You are generally free to name the files in /{name}/templates as you wish. However, there are a few exceptions:

/{navn}/templates/global.html

The parent page that contains the structure of the entire shop. The content itself is inserted where this template shows <{$contents}>, which is a special variable that comes from the index.html below. A very simpleglobal.html could look like this:

<!DOCTYPE html>
<html>
<head>
	<title><{$webshop->getName()|escape}></title>
</head>

<body>

	<nav>
		<!-- Din navigation -->
	</nav>

	<div>
		<{$contents}>
	</div>

</body>

</html>

/{navn}/templates/index.html

This template is used to display the content of whatever you're looking at. That could be a product, a landing page, or the cart, and so on. A suggestion for how it could look is the following:

<{if $inc}>

	<{include file=$inc}>

<{elseif $category}>

	<{include file="category.html"}>

<{elseif $landing_page}>

	<{include file="landing_page.html"}>

<{elseif $product}>

	<{include file="product.html"}>

<{elseif $page}>

	<{include file="page.html"}>

<{elseif $blog_post}>

	<{include file="blog_post.html"}>

<{/if}>

/{navn}/templates/printed_invoice.html

Used from admin if you want to print a delivery note. The content of the order will always be in the $order variable.

/{navn}/templates/mails

Folder for mail templates. Should contain the following files:
after_purchase.html

If you want to send your customers an email some time after they have made a purchase.

Relevante variabler: $products $order
basket_mail.html

Email that can be automatically sent if the customer has entered their email address but has not checked out.

Relevante variabler: $basket_url $basket
in_stock_mail.html

Email that can be sent if an item is back in stock and the customer has signed up.

Relevante variabler: $product
invoice_dropshipping.html

Email to dropshipping supplier.

Relevante variabler: $products $order
invoice.html

The invoice for the order.

Relevante variabler: $order
order_credit.html

Email if the order is credited.

Relevante variabler: $order
order_sent.html

Email sent when the order is shipped.

Relevante variabler: $order
order_ready_for_pickup.html

Email sent when the order is ready for pickup in store.

Relevante variabler: $order
order_picked_up.html

Email sent when the order is picked up from the store.

Relevante variabler: $order
payment_mail.html

If the customer has completed an order, but has not completed the payment.

Relevante variabler: $payment_url $order
product_review_mail.html

Email that can be sent to the customer if you want reviews.

Relevante variabler: $order
user-reset-password-mail.html

Email with link to reset password. To get the URL, use $customer->getTokenUrl().

Relevante variabler: $customer
user-welcome-mail.html

Welcome email if the customer creates an account in the shop.

Relevante variabler: $customer
user-points-expires-mail.html

Email sent to the customer if the points expire.

Relevante variabler: $customer $points_to_expire $points_expire_date
return_received.html

Email sent to the customer when they create a return via the return center.

Relevante variabler: $order_return
return_label.html

Email sent to the customer when they create a return via the return center.

Relevante variabler: $order

General information about the structure of mail templates

By default, all mail templates are divided into two parts. One part for the subject and the other part for the content. The way the template is divided is by checking if the variable $subject is set. If it is, the template should return the subject. Otherwise, it should return the content. Example:

<{if $subject}>

	Emnet på mailen

<{else}>

	Indholdet af mailen

<{/if}>

With the exception of the two emails invoice.html andinvoice_dropshipping.html where the subject is controlled via adminadmin and not via the template.

Images, stylesheets, etc. in the theme

You are allowed to place your images, stylesheets, etc. wherever you want, but we recommend that you use a structure like /{name}/images and /{name}/css. When referring to the files, use the variable $theme_url which contains the URL to the theme. Note that it can change, so you cannot always manually enter the URL into your theme. But the variable will always work. If you want to link to the image /{name}/images/img.gif, you should therefore write .

Extensions

Shoporama contains various data types such as products and categories. These data types include predefined fields such as title, description, and price. It is possible to expand the most common data types with custom fields. Extensions should be placed in the theme in the file /{name}/extensions/{datatype}.json, where the possible data types are product, landing_page, category, static_page, and blog_post.

The json-file must contain an array of groups. Each group contains a row of fields. The format is as this:

name string

Group field name

description string

Longer description

fields array

Array of fields

Field properties:

title

The title of the field

description

Longer description if relevant.

id

ID to fetch value with

type

One of the following types:

multi list text richtext longtext number bool image images color date datetime repeater
options

Values whose type is either list or multi. The format is

"options": {
	"foo": "Foo",
	"bar": "Bar",
	"baz": "Baz"
}
placeholder

Optional placeholder

default

Default value

repeater

Bruges til at oprette gentagne grupper af felter som kunden kan tilføje/fjerne dynamisk. Repeater items kan trækkes og slippes for at ændre rækkefølgen. Kræver et fields array der definerer sub-felterne.

Ekstra properties:

  • field_title - Navn på enkelte item (fx "Feature", "Slide", "Punkt")
  • fields - Array af sub-felter (understøtter: text, richtext, longtext, number, image)

Below is an example of json in the file extensions/product.json:

[
	{
		"name": "Billeder",
		"description": "Her kan du tilføje billeder",
		"fields": [
			{
				"title": "Et enkelt billede",
				"id": "my-image",
				"type": "image"
			},
			{
				"title": "En serie af billeder",
				"id": "my-images",
				"type": "images"
			}
		]
	},
	{
		"name": "Indstillinger",
		"description": "Herunder kan du indstille diverse",
		"fields": [
			{
				"title": "my-setting",
				"description": "Vælg flere ..",
				"id": "tags",
				"type": "multi",
				"options": {
					"foo": "Foo",
					"bar": "Bar",
					"baz": "Baz"
				}
			}
		]
	},
	{
		"name": "Features",
		"description": "Produktfeatures med ikoner",
		"fields": [
			{
				"title": "Features",
				"id": "features",
				"type": "repeater",
				"field_title": "Feature",
				"description": "Tilføj features for produktet",
				"fields": [
					{
						"id": "title",
						"type": "text",
						"title": "Overskrift",
						"placeholder": "Fx: Vandtæt"
					},
					{
						"id": "description",
						"type": "longtext",
						"title": "Beskrivelse"
					},
					{
						"id": "icon",
						"type": "image",
						"title": "Ikon"
					}
				]
			}
		]
	}
]

Will produce this interface:

Extension form example

For data types that support extensions, the values must be retrieved via the getExtensionValue('ID') method, where ID is the ID from the json file. Depending on the data type of the field, the content can be returned as a text, array, or object:

Materiale: <{$product->getExtensionValue('materiale')}>

Or:

<{if $img = $product->getExtensionValue('img')}>
	<{$img->getSrc(50, 50, 'fit')}>
<{/if}>

Or if you want to loop images:

<{foreach $product->getExtensionValue('my-images') as $image}>
	<img src="<{$image->getSrc(50, 50, 'box')}>">
<{/foreach}>

For repeater felter kan man loope gennem items. Image felter i repeaters returneres som Image objekter:

<{foreach $product->getExtensionValue('features') as $feature}>
	<div class="feature">
		<{if $feature.icon}>
			<img src="<{$feature.icon->getSrc(64, 64, 'box')}>" alt="">
		<{/if}>
		<h3><{$feature.title|escape}></h3>
		<div><{$feature.description|escape|nl2br}></div>
	</div>
<{/foreach}>

For date and datetime fields, the value can be used directly or formatted with Smarty's date_format:

<{* Date felt - vis kun dato *}>
Lanceringsdato: <{$product->getExtensionValue('publishdate')|date_format:'%d/%m/%Y'}>

<{* Datetime felt - vis dato og tid *}>
Publiceringsdato: <{$product->getExtensionValue('publishdatetime')|date_format:'%d/%m/%Y kl. %H:%M'}>

<{* Tjek om produktet skal vises baseret på dato *}>
<{if $launch_date = $product->getExtensionValue('publishdate')}>
	<{if $launch_date|strtotime <= $smarty.now}>
		<!-- Produktet er lanceret -->
	<{/if}>
<{/if}>

<{* Sammenlign datoer *}>
<{if $product->getExtensionValue('publishdatetime')|strtotime > $smarty.now}>
	Kommer snart: <{$product->getExtensionValue('publishdatetime')|date_format:'%d. %B %Y'}>
<{/if}>

Global variables

$webshop

General object that contains the webshop, but also contains some functions for extracting various data from the webshop. Read more about the Webshop object. Example of how $webshop is used to pull a list of categories:

<{foreach $webshop->getCategories() as $category}>
	<{$category->getName()|escape}>
<{/foreach}>
$product

Product object if you see a product page

$category

Category-object if a category is shown

$landing_page

Dynamic category-object if a dynamic category is shown

$page

Object for static page.

$blog_post

Blog post object if you are looking at a blog post

$inc

The name of the special - non-dynamic, page being executed. The values for this can be return.html, order-return.html, return_received.html, return_label.html, also.html, search.html, basket.html, address.html, shipping.html, approve.html, payment.html, thanks.html, order.html, product_review.html, subscription.html, blog.html, user-sign-up.html, user-sign-in.html, user-sign-out.html, user-edit.html, user-reset-password.html, user-profile.html, user-orders.html, user-points.html, user-subscriptions.html, user-change-card.html, 404.html, 410.html.

Since we validate the content, you can use the code below to control the display in an include file:

<{if $inc}>
	<{include file=$inc}>
<{/if}>
$shipping

Selected shipping method

$pager_array

Pager if the current page contains page scrolling. The contents of the array are the elementsmax,current,total,url, andfirst_url.

$pager

Pager object

$current_url

Absolute URL for the current page

$top_url

Relative URL to the current page.

$get

An array with all the GET variables.

$post

An array with all POST variables.

$cookie

An array with all COOKIE variables.

$user_id

The ID of the shop owner who is logged into admin. This variable is set if you follow a link to the shop from admin.

$customer

Customer object if the customer is logged in to the shop.

$remote_addr

The IP address of the customer viewing the page.

$admin_url

URL to Shoporama admin

$selected_payment_gateway

The ID of the selected payment gateway.

$join_mailinglist

An indicator indicating whether the customer has marked that they want to sign up for the newsletter.

$basket_url

URL to the basket and its contents.

$products_matches

IDs of products that have matched a discount or promotion

$campaign_ids

IDs of campaigns if the products in the cart match a campaign.

$campaigns

If there are active campaigns.

$campaign_discount

The discount from the active campaigns.

$campaign_matches

An array of products that match a campaign. If there are no products, the variable is null.

$unpaid_order

If the customer has an unpaid order.

$unpaid_recurring_order

Unpaid subscription.

$basket

The content of the cart as an array, where the individual elements are the products in the cart, with the values:

.id

unique id on the line

.product_id

The ID of the product

.in_stock

true/false if the product is in stock

.product

the product

.attributes

the product's attributes

.own_id

the item number

.amount

the quantity in the cart

.comment

evt. kommentar - optional comment

.bundle

array of the product if the product is a bundle

Example of how the above is used:

<{foreach $basket as $row}>
	<{if $image = $row.product->getImage()}>
		<img src="<{$image->getSrc(50, 50, 'box')|escape}>">
	<{/if}>

	<{$row.amount}> x <{$row.product->getName()|escape}>

	i alt

	<{$row.product->getRealPrice($row.amount, $row.attributes)|number_format:2:",":"."}>
	<{$webshop->getCurrency()}>

<{/foreach}>

Check the Template API to see which methods are available on the different objects.

$subscriptions

Basket subscriptions. Works in the same way as $basket

$price

The subtotal of the cart's contents. That is, without shipping.

$shipping_price

The shipping cost of the cart.

$total_price

Total price of the content in the cart.

$vat

The VAT on the contents of the cart.

$basket_weight

The total weight of the curve (for e.g. shipping).

$total_amount

Total number of products in the curve.

$voucher

The discount code if there is one.

$voucher_discount

The discount from the discount code.

$shipping_country

The selected delivery country.

$nofollow

Indicates whether nofollow is set on the page.

$meta_title

The title of the page.

$meta_description

Description of the page.

$canonical

URL of the page's canonical.

$session_order

Array with the billing address. Contains the fieldsname, company_name, vat_number, address, address2, zipcode,city, email, phone, ean_number, comments

$session_del

Array with the delivery address. Contains the fields:name, company_name, address,address2, zipcode, city

$session_extra

Array of extra fields on the order. If a field is POSTed withextra[test]=123, $session_extra.test will be 123 and will be stored on the order.

$use_points

Number of points the customer has entered that they want to use on the order.

$point_discount

How many DKK (or other currency) the used points (from $use_points) correspond to.

$earns

How many points the customer earns by completing the order.

Template functions

The following are the Shoporama-specific features that can be used in templates:

t

Template language support

It is possible to build language support into your templates. Not classic language support, where you can select the text in multiple languages, but language support where it is possible to change the text in admin. It works by wrapping the text in a <{t}> block. After the template has been rendered in the browser, the text can then be edited in admin. Example:

<{t}>Din kurv<{/t}>

Note that if you change the text in your template files, the new text will appear in admin as a new text. There are no IDs or names other than the content, which makes the text unique.

You can set the section attribute on the block to divide your texts into more manageable parts. As well as hint if you want to give a hint in admin when the text is edited:

<{t section="basket" hint="Bruger i overskriften"}>Din kurv<{/t}>

If you have variable content that you want to send with without the value being in admin, you can use the syntax below:

<{t amount=$total_amount price=$total_price|number_format:2:",":"." hint="Til kurven"}>
    Du har {amount} ting til {price} kroner i kurven.
<{/t}>

If it fits better in your structure, you can also use t as a modifier on your variables/functions:

<{$title = "Min titel her"}>
<{$title|t}>

The above will display the following in admin:

Smarty translation example
Arguments
section

Section for partitioning in admin

hint

Hint used in admin as help text

cache

Template caching

If a template contains a lot of heavy calls that don't need to be updated live - such as variants on products, menus, or category overviews, Shoporama includes a caching function that can make the shop a lot faster. The functions used are cache and get_cache respectively, which first mark the part to be cached and later retrieve the cached data. Example:

<{get_cache name="my_cache" ttl=3600 assign="c"}>

<{if $c}>

	Fra cache: <{$c}>

<{else}>

	Live:

	<{cache name="my_cache"}>
		<{$smarty.now}>
	<{/cache}>

<{/if}>

The cache function wraps the content and assigns it the name specified in name. When the content is to be extracted, get_cache is used, which takes the arguments name, as used earlier, and ttl, which specifies in seconds how old the content can be. If the argument is omitted, the default is one hour.

Arguments
name

Used to specify the name of the cached item. It is possible to use dynamic names from variables.

get_cache

Cache content retrieval

Function that returns the cached content from the cache function.

Arguments
name

The name of the cached item used in cache.

ttl

Time To Live. Number of seconds the cache is allowed to remain valid for.

assign

The name of the variable to which the content should be assigned.

Theme settings

It is possible to add some general settings to themes by adding a theme_settings.json in the root of the theme, and it will result in something similar to the following:

Theme settings example

The structure is as follows:

info

The name of the theme

string

features

Array of features the theme contains. Only used for viewing in admin. Only useful if the theme is general and multiple users can select it. For example ["Feature A", "Feature B", "Feature C"]

array

demo

URL to possible demo page

string

settings

Array of editable fields

array
.path

ID of fields included in the variable as the first element when retrieving the value. If path is set to design, the values will be in $settings.design.{field name}

string
.name

Name displayed in admin

string
.description

Description shown in admin

string
.fields

The individual fields that can be edited by the user

array
.path

The ID for when to retrieve the value. If path is set to color and the parent field was design, the field can be retrieved via $settings.design.color

string
.name

Name displayed in admin

string
.description

Description shown in admin

string
.default

Default value. If the field is empty, this value will be used.

string
.type

The type of the field. The valid types are: image, color, string, bool, wysiwyg, and list.

string
.values

Values if type is set to list. Formater er følgende:

[
	{
		"name": "cheese",
		"value": "Ost"
	},
	{
		"name": "ham",
		"value": "Skinke"
	}
]
array

Example of file contents

{
	"info": "Mit tema",
	"settings": [
		{
			"path": "design",
			"name": "Design",
			"description": "Indstillinger for dit design",
			"fields": [
				{
					"path": "logo",
					"name": "Toplogo",
					"description": "Upload dit logo her",
					"type": "image"
				},
				{
					"path": "background_color",
					"name": "Baggrundsfarve",
					"description": "Baggrundsfarven på shoppen",
					"type": "color"
				},
				{
					"path": "size",
					"name": "Bredde",
					"description": "Bredden på siden",
					"type": "list",
					"values": [
						{
							"name": "Afgrænset",
							"value": "boxed"
						},
						{
							"name": "Fuld bredde",
							"value": "fullwidth"
						}
					]
				}
			]
		},
		{
			"path": "contact",
			"name": "Kontaktoplysninger",
			"description": "Dine kontaktoplysninger der vises i temaet",
			"fields": [
				{
					"path": "mail",
					"name": "E-mail-adresse",
					"description": "Indtast din e-mail-adresse her",
					"type": "string"
				}
			]
		},
	]
}

Page Builder

The Page Builder is a tool for building content through sections based on JSON files. These sections can be placed on different pages in the webshop. The pages in which they can be placed are defined in the file /{name}/components/sections.json. The content of each section is defined in their own respective file, which is placed in /{name}/components/sections/{section}.json.

If you have done the above, the Design link at the top of the admin will automatically lead to the Page Builder.

Page builder example

The structure in the construction of sections is as follows:

/{navn}/components/sections.json

File: sections.json

Contains a list of page types and an array of which sections are allowed. In addition to page types, you can also use custom tags.

{
	"Sidetype": ["sektionA", "sektionB"]
}

The following types of pages exist:

product

Product pages

category

Categories

landing_page

Dynamic categories

static_page

Pages

blog_post

Blog post

basket

The basket

address

Address page

shipping

Shipping page

approve

Approval page

thanks

The thank you page, which the customer typically sees after the order has been completed.

search

Search result page

also

The upsell page, which can be used if you show an upsell page after the customer has added products to the cart

order

Order page, which displays the contents of the order via a link

#tags

Optional tags you create yourself

An example of a sections.json where the user can add the sectionsslider, photos, and quiz to landing pages, but only slider to product pages, and on #footer an about section can be added would look like this:

{
	"landing_page": ["slider", "photos", "quiz"],
	"product":      ["slider"],
	"#footer":      ["about"]
}
/{navn}/components/tags.json

File: tags.json

Tags are used for locations that you invent yourself and that you can drag out as you please. Maybe you need a #footer that you drag out and display at the bottom of the shop, or an #xmas setup that you only use in December. The format for tags.json is:
{
	"tag1": "Beskrivelse ...",
	"tag2": "Beskrivelse ...",
	"tag3": "Beskrivelse ..."
}

The individual tags automatically appear in the admin when the user creates a new configuration.

Tags example

To extract the content of tags in the theme, thegetBlocks() function is used as follows:

<{if $blocks = $webshop->getBlocks("#foo")}>
	<{foreach $blocks as $block=>$elements}>
		[...]
	<{/foreach}>
<{/if}>
/{navn}/components/sections

Library: sections

In this directory, each section is placed in its own json file. Each file is named after the name of the section. If the section is called slider, the content should be located in/{name}/components/sections/slider.json. The structure of the files is as follows
title

Field: title

The name displayed in admin

string
description

Field: description

Description shown in admin

string
fields

Field: fields

An array of fields

array
Properties for array elements:
.id

The ID of the field. Used when the content is looped through in the theme.

string
.type

Content type. Valid types are:list,text,integer,image,images,bool,richtext,longtext,repeater,product,category, andlanding_page

string
.options

Only used for fields of type list and it contains a series of options such as

[...]
"options": {
	"hat": "Hat",
	"glasses": "Briller",
	"beard": "Skæg"
},
[...]
string
.title

The title of the field

string
.description

Description of the field

string
.max

Maximum number of fields that can be added

int
.required

If the field is required

bool
.placeholder

Placeholder for admin

string
.default

Optional default value for the field

string
.fields

Only used if the type is repeater and can contain the same fields as sections. A simplified example of a slider repeater could be:

[...]
"type": "repeater",
"fields": [
	{
		"id": "headline",
		"type": "text",
		"title": "Overskrift"
	},
	{
		"id": "img",
		"type": "image",
		"title": "Billedet"
	}
]
[...]
string
.field_title

Only used if the type is repeater and contains the name of the individual elements in the repeater

string

Views

There are different strategies for extracting data. As a starting point, there will be a $page_blocks on the pages that match a setup. It can be looped through, or the content can be extracted using $webshop->getBlocks('#foo'). Alternatively, the content can be accessed through the name of the section.

As a starting point, we would recommend that you place the display of each section in separate files, such as /{name}/templates/sections/{section}.html. Then it is easy to loop through $page_blocks and include the correct display as follows:

<{if $page_blocks}>

	<{foreach $page_blocks as $section}>
		<{$type = $section._type}>

		<{if $webshop->templateExists("sections/$type.html")}>

			<{include file="sections/$type.html"}>

		<{/if}>

	<{/foreach}>

<{/if}>

Note that the row type is in the variable _type

Below section calls images – hence content is in /{navn}/components/sections/images.json.

{
	"title": "Overskrift og billeder",
	"fields": [
		{
			"id": "headline",
			"type": "text",
			"title": "Overskrift"
		},
		{
			"id": "images",
			"type": "images",
			"title": "Billeder"
		}
	]
}

It will produce the following interface in admin

Images example

To extract content through the above method may /{navn}/templates/sections/images.html contain the following:

<h1><{$section.headline|escape}></h1>

<{foreach $section.images as $image}>
	<img src="<{$image->getSrc(150, 150, 'box')}>">
<{/foreach}>

Regarding data type, it is important to be aware of what each field contains. It can be either a string, array, or object. Therefore, one should do something similar:

[...]
<{foreach $section as $name=>$val}>
	<{if is_array($val)}>
		Repeater or array of images
	<{elseif is_object($val)}>
		An image
	<{else}>
		Text, number or such
	<{/if}>
<{/foreach}>
[...]

If you use the types product, category, or landing_page, the selected ID will be returned in the theme, and you have to extract the object yourself using the methods $webshop->getProductById(id), $webshop->getCategory(id), or $webshop->getLandingPage(id). For example:

[...]
<{if $product = $webshop->getProductById($section.product_id)}>
	<{$product->getName()|escape}>
<{/if}>
[...]

Please note that it is always possible to use var_dump to examine what the variable contains. That way you can loop through it correctly.

Views

Based on the previously mentioned structure, the individual views are reviewed below. To understand the individual objects and which methods are available, we recommend looking in our. Template API.

Download our Alaska theme to see examples of the different views.

Products

The display of products can be located in product.html, but it is optional. It is always index.html that executes the individual displays. However, we recommend using this structure. To know if a product view is being executed, it checks if $product is present.

Add to cart

Products are added to the cart by making a POST call to any page - typically just the page already being displayed, with the product_id, attributes[{attribute_id}]={attribute_value_id}, and amount arguments. The attribute should only be added if the products have variants.

A simple example of how to add products to basket may be:

<form action="" method="post">
	<input type="hidden" name="product_id" value="<{$product->getProductId()}>"/>
	<input type="number" name="amount" value="1" min="1"/>
	<input type="submit" value="Læg i kurv">
</form>

With variants

<form action="" method="post">
	<input type="hidden" name="product_id" value="<{$product->getProductId()}>"/>

	<{foreach $product->getProfile()->getAttributeList() as $attribute}>
		<{if $attribute->getIsVariant() && $attribute->getDataType() == "valuelist"}>
			<select name="attributes[<{$attribute->getAttributeId()}>]">
				<{foreach $attribute->getValues() as $value}>
					<option value="<{$value->getAttributeValueId()}>">
						<{$attribute->getName()|escape}>: <{$value->getVal()|escape}>
					</option>
				<{/foreach}>
			</select>
		<{/if}>
	<{/foreach}>

	<input type="number" name="amount" value="1" min="1"/>
	<input type="submit" value="Læg i kurv">
</form>

The above can of course be made more complicated if you wish to display stock availability, bundled packages, etc.

Dynamic categories

Landing pages contain a $landingpage and $products which is an array of products to be displayed on the page.

If a pager is needed it will be included this way

<{if $pager}>
	<{$pager->render()}>
<{/if}>

Categories

Categories contain a $category and $products which is an array of products to be displayed on the page.

If a pager is needed it will be included this way

<{if $pager}>
	<{$pager->render()}>
<{/if}>

Pages

Static pages will contain a $page.

Blog

The blog can be divided into two pages: blog.html and blog_post.html, where the first one can retrieve a list of blog posts through:

<{$blog_posts = $webshop->getBlogPosts()}>

Viewing the individual blog posts is through the variable $blog_post

If the list of blogposts is from a category there will be a $category available at the category.

Product reviews

In the product_review.html file, there is access to $order, which represents the order. You can only review products that you have purchased. Based on the order, you can retrieve the products using $order->getOrderProducts(). To save the product reviews, you need to make a POST call to the same page with the following content:

<{if $order}>
	<{if $get.voted}>
		Tak!
	<{else}>
		<form action="" method="post">
			<{foreach $order->getOrderProducts() as $product}>
				<{$product->getName()|escape}>
				Stjerner:
				<input type="number" min="1" max="5" name="rating[<{$product->getProductId()}>]">
				Anmeldelse
				<textarea name="description[<{$product->getProductId()}>]"></textarea>
			<{/foreach}>
			<input type="submit" value="Skriv anmeldelse"/>
		</form>
	<{/if}>
<{/if}>

$get.voted is set to true when the customer has written a review.

To link to the review page from your order use $order->getReviewUrl().

The search function works by making a GET request to /search where the argument ?search= should be your search keyword. E.g.:

<form action="/search">
	<input type="text" name="search" value="<{$get.search|escape}>"/>
	<input type="submit" value="Søg"/>
</form>

The search is done in both products, categories, blog posts, and landing pages. To display the result, you need to look at the four variables $products, $categories, $blog_posts, $pages, and $landing_pages.

If searching in static pages is to be enabled, it must be activated in the admin.

Wishlist

If your customers can log in to your shop, you can provide them with the option to create wishlists. They can create as many wishlists as they want, and a wishlist has a name, a description, and a number of products. The wishlist itself has a public address so your customers can share a link.

The wishlist uses the following templates:

wishlist.html
URL: /wishlist

The page with the public wish list.

Relevante variabler:

$wishlist that contains the wish list. See methods here.

user-wishlists.html
URL: /user-wishlists

The page with the user's wishlists. Requires login.

Relevante variabler:

$customer->getWishlists() to extract wishlists.

user-wishlist.html
URL: /user-wishlist

The page used to edit the wishlist and products.

Relevante variabler:

$wishlist that contains the wish list. See methods here.

To create a wishlist, a POST request with the field name and optionally description needs to be sent from user-wishlists.html. For example,

<form action="" method="post">
    Navn
    <input type="text" name="name">

    Beskrivelse
    <textarea name="description"></textarea>

    <input type="submit" value="Opret">
</form>

Note that you can advantageously use the methods $wishlist->getRemoteUrl() and $wishlist->getEditUrl() to create links for displaying and editing the wishlists.

In the file user-wishlist.html, where the wishlist is edited, the name and description should be sent as name, description, and status which can be either active or closed. The products themselves, along with their quantity and optional comment, should be sent as an array in the following format:

wishlist_product[{id}][name] = 'navn'
wishlist_product[{id}][comments] = 'kommentar'
wishlist_product[{id}][remove] = '1' // kun hvis produktet skal fjernes

Example code:

<{foreach $wishlist->getWishlistProducts() as $wp}>
    <{* Fordi der er forskel på produkter og ønskelisteprodukter }*>
    <{$product = $wp->getProduct()}>

    <input type="checkbox" name="wishlist_product[<{$wp->getWishlistProductId()}>][remove]" value="1">

    <a href="<{$product->getUrl()|escape}>"><{$product->getName()|escape}></a>

    <{if $variant = $wp->getVariantValue()}>
        <{$wp->getVariantName()|escape}>: <{$variant|escape}>
    <{/if}>

    <input type="number" name="wishlist_product[<{$wp->getWishlistProductId()}>][amount]" value="<{$wp->getAmount()}>">

    <textarea name="wishlist_product[<{$wp->getWishlistProductId()}>][comments]"><{$wp->getComments()|escape}></textarea>

<{/foreach}>

If a variable edit is sent, the page will return to the same editing page again. Otherwise, it will be redirected to the overview of wish lists. To delete the wish list, a variable named remove must be sent. Example:

<input type="submit" name="remove" value="Slet" onclick="return confirm('Er du sikker?');">

To add products to a wishlist from the shop, a POST request can be made from any page with the fields wishlist_id, product_id, and an optional attribute_value_id if the product has variants. Example:

<{if $product && $customer}>

    <{if $wishlists = $customer->getWishlists()}>

        <form action="" method="post">
            <input type="hidden" name="product_id" value="<{$product->getProductId()}>"/>

            <label>Vælg ønskeliste:</label>
            <select name="wishlist_id">
                <{foreach $wishlists as $wishlist}>
                    <option value="<{$wishlist->getWishlistId()}>">
                        <{$wishlist->getName()|escape}>
                    </option>
                <{/foreach}>
            </select>

            <{if $variant = $product->getVariant()}>
                <label>Vælg variant:</label>

                <select name="attribute_value_id">
                    <{foreach $variant->getValues() as $value}>
                        <option value="<{$value->getAttributeValueId()}>">
                            <{$variant->getName()|escape}>: <{$value->getVal()|escape}>
                        </option>
                    <{/foreach}>
                </select>
            <{/if}>

            <input type="submit" value="Tilføj til ønskeliste">
        </form>

    <{/if}>

<{/if}>

Return center

The returns center allows your customers to create returns themselves. It needs to be activated in the shop settings. Once a customer has created a return, it needs to be approved in the admin, and a credit note will be issued.

The return center uses the following templates:

return.html

Template: return.html

URL: /return

The page where the return is created

Relevant variables:

$order containing the order the customer wants to return. See methods here.

order-return.html

Template: order-return.html

URL: /order-return

The page where the return is subsequently displayed

Relevant variables:

$order_return with the return. See methods here.

return_received.html

Template: return_received.html

URL: Email template

Email sent to the customer when they create a return

Relevant variables:

$order_return with the return. See methods here.

There are two ways for your customer to access the return page. One is by entering the order number and email address associated with their order. The other is by following a direct link. The direct link can be retrieved from the order using the $order->getReturnUrl() function. For example,

<{if $webshop->useReturnCenter() && !$order->getIsCreditNote()}>
	<a href="<{$order->getReturnUrl()|escape}>">Retuner varer</a>
<{/if}>

As seen in the example, it is a good idea to check that the order is not a credit note and if the webshop has activated the return center. The example can be used anywhere there is an $order, such as the customer's order overview or in emails.

If you do not use login or in any other way want the customer to be able to search for their order, you need to implement a search with order number and email address on return.html. The fields webshop_order_id and email should be POSTed to /return. If the order is found, the customer will be redirected to the return process. If not, $get.not_found will be set, and you can display an error message in the theme. A minimal form can look like the following:

<{if $get.not_found}>
    <p>Der blev ikke fundet nogen ordre.</p>
<{/if}>

<h1>Returcenter</h1>

<form method="post" action="">
    <input type="number" name="webshop_order_id" placeholder="Ordrenummer">
    <input type="email" name="email" placeholder="E-mail-adresse">

    <input type="submit" value="Søg">
</form>
Creation of return

In the file return.html, a list of order lines must be extracted, and the customer can choose which products to return and why. Unlike regular order views, the return process should include one line per order line.

<form action="" method="post">
    <{$line = 0}>

    <!-- Looper ordrelinjerne igennem -->
    <{foreach $order->getOrderProducts() as $product}>

        <!-- getReturned() returnerer antallet der tidligere er returneret -->
        <{$returned = $product->getReturned()}>

        <!-- Løkke for hver antal -->
        <{section name="i" loop=$product->getAmount()}>
            <{$line = $line+1}>

            <!-- Viser ikke flere end det er muligt at returnere -->
            <{if $line - $returned > 0}>

                <{$product->getName()|escape}>

                <{if $attributes = $product->getAttributes()}>
                    <{foreach $attributes as $attribute}>
                        <{$attribute.name}>: <{$attribute.val}>
                    <{/foreach}>
                <{/if}>

                <!-- Check hvilken pris der skal vises -->
                <{if $webshop->getUseCalculatedUnitPrice()}>
                    <{$webshop->getCurrency()}> <{$product->getCalculatedUnitPrice()|number_format:2:",":"."}>
                <{else}>
                    <{$webshop->getCurrency()}> <{$product->getUnitPrice()|number_format:2:",":"."}>
                <{/if}>

                <!-- Checkbox og en grund -->
                <input type="checkbox" name="return[<{$line}>]" value="<{$product->getOrderProductId()}>">Vælg
                <input type="text" name="reason[<{$line}>]" placeholder="Evt. årsag til returnering"/>

            <{/if}>
        <{/section}>

     <{/foreach}>
</form>

It is possible in admin to specify a fixed delivery method and price. If this is done, the delivery method is retrieved via $webshop->getReturnShipping($country_id), and if the price is fixed, it is retrieved via $webshop->getReturnShippingPrice($country_id). For example:

<{if $return_shipping = $webshop->getReturnShipping($order->getDelCountryId())}>
    Varer skal returneres via <{$return_shipping->getName()|escape}>.

    <{if $price = $webshop->getReturnShippingPrice($order->getDelCountryId())}>
        Prisen er <{$price|number_format:2:",":"."}>
    <{else}>
        Prisen er <{$return_shipping->getCost()|number_format:2:",":"."}>
    <{/if}>
<{else}>
    <!-- Vis en liste over shoppens almindelige leveringsmetoder her -->
<{/if}>

The selected delivery method, if optional, should be sent as shipping_id.

Display of return

Once the return is created, it will appear in order-return.html, which contains an $order_returnwith the relevant methods.

/also

also.html is our page for upselling. As a starting point, there is access to $product, and based on that, suggestions can be made to the customer. It may be relevant to use $product->getRelatedProducts(), $product->getAlsoBought(), or $webshop->getPopularProducts().

404

Only used to show 404 pages

410

Only used to show 410 pages

Captcha

An automatic captcha is inserted when you try to sign up for a newsletter or write comments. This is to prevent spam. It is possible to design the page itself in captcha.html, and it must contain at least:

<form action="" method="post">
	<{$form}>

	<{if $error}>Fejl i koden<{/if}>

	<img src="<{$imgstr}>">

	Skriv indholdet at ovenstående felt:
	<input type="text" name="c">

	<input type="submit" value="OK">
</form>

Login

It is possible to allow customers to log in to the shop. If the person is logged in, there will be a $customer that contains information about the user. All pages that edit, log in, etc. should submit via POST to the same page.

/user-edit.html

Uset to edit customer data. It is possible to add fields to the customer. View more under this table.

Relevante variabler:
company vat_number name email phone address zipcode city country_id pass1 pass2
/user-orders.html

Customer's previous orders

Relevante variabler:
$my_orders
/user-points.html

Customer's point

Relevante variabler:
$my_points
/user-profile.html

Showing customer data

Relevante variabler:
$customer
/user-reset-password.html

Used to reset password

Relevante variabler:
email password
/user-sign-in.html

Login page. Use redir if the customer shall be redirectet to a certain URL after login.

Relevante variabler:
email password redir
/user-sign-up.html

A form used to sign up.

Relevante variabler:
name email phone address zipcode city company vat_number country_id

Custom fields for customers

In admin it is possible to define custom fields for users. These fields are edited this way:

<{foreach $webshop->getCustomerFields() as $field}>
	<{if $field->getType() == "list"}>
		<{$field->getName()|escape}>
		<select name="field[<{$field->getCustomerFieldId()}>]">
			<{foreach $field->getValues() as $value}>
				<option <{if $field->getVal() == $value}>selected="selected"<{/if}> value="<{$value|escape}>">
					<{$value|escape}>
				</option>
			<{/foreach}>
		</select>
	<{elseif $field->getType() == "string"}>
		<{$field->getName()|escape}>
		<input type="text" name="field[<{$field->getCustomerFieldId()}>]" value="<{$field->getVal()|escape}>">
	<{/if}>
<{/foreach}>

Loyalty programme

The loyalty program allows your customers to earn and use points when they place orders. The setup is managed under Customers > Loyalty Program.

You can use the following methods to check if the shop has enabled the loyalty program and if the customer is logged in and has earned points they can use:

<{if $webshop->hasLoyaltyProgram() && $customer && $customer->getActivePoints()}>
	[Visning af point m.m.]
<{/if}>

To display the number of points the customer has earned, as well as how many they can use on the current order, you can use the following code example:

Du har optjent <{$customer->getActivePoints()|number_format:0:",":"."}> point.

<{if $points = $customer->getPointsAvailable()}>
	Du kan bruge <{$points|number_format:0:",":"."}> point på denne ordre.
<{else}>
	Du har ingen optjente point du kan bruge på denne ordre.
<{/if}>

The field you POST to the cart, indicating how many points the customer wants to use, should be named use_points, and can appear as follows:

<input type="number" name="use_points" value="<{$use_points}>" min="0" max="<{$customer->getPointsAvailable()}>"/>

You can use $earns to see how much the customer earns on the order:

Du optjener <{$earns|number_format:0:",":"."}> point på denne ordre.

You can use the example below to describe your loyalty program:

Du optjener <{$webshop->getLoyaltyProgramBasePoints()|number_format:0:",":"."}> point hver
gang du køber for 1 <{$webshop->getCurrency()}>. Når du betaler med point svarer
<{$webshop->getLoyaltyProgramBaseCost()|number_format:0:",":"."}> point til 1 <{$webshop->getCurrency()}>.

If you want to create a points overview page, you can use user-points.html, where you can loop through $my_pointsand present the content. Take a look at the SafePoint class to see what methods are available.

Tip: You can style your order overview by using the following CSS classes: points_used, points_earned, and has_profile.

Check-out

The check-out flow is simply a series of HTML pages, each containing a form that POSTs some data to itself. If the next field is set, Shoporama will proceed to the next page in the flow. Typically, the "Next" button will have name="next". It is always possible to link to previous pages in the flow. When the customer enters their information, it is stored in two arrays: $session_order and $session_del. For example, $session_order.name.

basket.html

File name: basket.html

Shows basket content. Simpel example of how to use $basket

<{foreach $basket as $line}>
	<{$line.amount}> x <{$line.product->getName()}>

	<{foreach $line.attributes as $a}>
		<{$a.name|escape}>: <{$a.value|escape}>
	<{/foreach}>
<{/foreach}>

To change the quantity, it should be POST'ed in the following way.

[...]
<input name="amount[<{$line.id}>]" value="<{$line.amount}>" />
[...]

To add a discount code, you need to POST a voucher with the discount code. If the discount code is invalid, $get.wrong_voucher will be present in the template.

The delivery country can be changed by POSTing a del_country_id with the ID of the delivery country.

Relevant variables/parameters in the form:
$basket $voucher $voucher_discount $campaign_ids $campaign_discount $price $shipping_price $total_price $vat
address.html

File name: address.html

The page where the delivery information is entered.

If mailinglist is set to 1, the customer is subscribed to the newsletter.

If create_profile is set to 1, a profile is created for the customer so they can log in later.

Relevant variables/parameters in the form:
order_country_id order_name order_company_name vat_number ean_number order_address order_zipcode order_city email phone comments del_name del_company_name del_address del_zipcode del_city del_country_id mailinglist create_profile
shipping.html

File name: shipping.html

Used to select the delivery method.

The individual shipping methods can be extracted via $webshop->getShippings().

To save the shipping method, a shipping_id must be POSTed with the ID of that shipping method. If the shipping method has a delivery shop associated with it, they are extracted via $shipping->getDeliveryShops(). You can select the delivery shop in the following way:

[...]
<{if $shipping->getModule()}>
	<select name="shop">
		<{foreach $shipping->getDeliveryShops() as $shop}>
			<option value="<{$shop.number}>">
				<{$shop.name|escape}>
				<{$shop.street|escape}>
				<{$shop.zip|escape}>
				<{$shop.city|escape}>
			</option>
		<{/foreach}>
	</select>
<{/if}>
[...]
Relevant variables/parameters in the form:
shipping_id shop
approve.html

File name: approve.html

The authorization page. Used to display the order.

If the shop contains multiple payment gateways, these can be selected on this page. They are retrieved via $webshop->getPaymentGateways() and submitted via payment_gateway_id to switch.

Relevant variables/parameters in the form:
$basket $shipping payment_gateway_id
thanks.html

File name: thanks.html

Thank you page. Used to display the completed order.

Relevant variables/parameters in the form:
$order

Ajax

Shoporama has two built-in ajax calls. One can extract products in various ways (filtering), and the other is a general and simple search that searches in products, categories, and landing pages.

Filtering

The filtering is located in the file /ajax which is located at the root of all shops.

atags

A list of tags on the attribute values, attribute values are settings on the attributes on the products' profiles. the values on atags must be separated by commas, but allow groupings separated by pipe as a logical OR.

ajax?atags=female,black|white
product_ids

List of product IDs separated by pipe.

ajax?product_ids=1|2|3|4
price_range

Returns products that are within a price range. The amounts should be separated by a pipe.

ajax?price_range=100|200
categories

A pipe-separated list of categories the products should be in. If the argument exclude=1 is set, a list of products that do not have the categories is returned.

ajax?categories=5|9|2
ajax?categories=5|9|2&exclude=1
sort

Sort order. The values popular, weight, name, price, created can be set. Whether the sort order should be ascending or descending is specified by setting sort_order to either asc (ascending) or desc (descending ).

ajax?sort=price&sort_order=desc
attribute_values

A list of IDs of the attribute values that the products should have. The list is separated by pipe. If the argument exclude=1 is set, a list of products that do not match is returned.

ajax?attribute_values=9|8|12
suppliers

Pipe-separated list of supplier IDs.

ajax?suppliers=8|2
landing_pages

Girl-separated list of IDs for landing pages the products should appear on.

ajax?landing_pages=5|24
extension.{id}

If the theme uses extended fields, products can be extracted based on them. Absolute searches are made, so the fields that make the most sense to use are bool, multi, number, and list. Pipe-separated values can be used if the products need to match just one of the values.

ajax?extension.foo=1&extension.tags=foo|bar
attribute_tags_in_stock

List of tags for attribute values that should be in stock. The list can be piped separated.

ajax?attribute_tags_in_stock=foo|bar
attribute_tags

List of tags to be placed on the products. The list can be pipe-separated.

ajax?attribute_tags=foo|bar
attribute_tag

Same as attribute_tags however just an single tag

ajax?attribute_tag=foo
force_categories

A pipe-separated list of categori IDs that must be set on all products.

ajax?force_categories=5|9|2
limit

Maximum amount of products to be returned

ajax?limit=10
offset

Starting position in relation to where in the list of products you wish to retrieve from.

ajax?offset=100&limit=10
meta

Pipe-separated list of extra fields you wish to see on the products in the result. If you set meta=_all, all will be returned.

ajax?meta=foo|bar
ajax?meta=_all
only_in_stock_variants

Sættes til 1 eller 0 afhængig af om hvert produkt i resultatet kun skal medtage varianter der er på lager. Default er 0.

ajax?only_in_stock_variants=1
include_meta

Sættes til 1 eller 0 afhængig af om man önske et ekstra meta-felt i resultatet der indeholder beskrivelser af produkterne i resultatet. Det er oplysninger om hvilke attributter, kategorier, og brands der findes. Default er 0.

ajax?include_meta=1
include_pagination

Sættes til 1 eller 0 afhængig af om man önske et ekstra pagination-felt i resultatet der indeholder paging oplysninger. Felterne i array'et er offset, limit, count, total. Default indstilling er 0.

ajax?include_pagination=1
pretty

Sættes til 1 eller 0 afhängig af om json-svaret skal formateres pænt eller ej.

ajax?pretty=1

The response from /ajax is an array in the following format:

[
    {
        "product_id": 139735,
        "own_id": "skunumme",
        "name": "Produktnavn",
        "supplier_id": 0,
        "supplier_name": "",
        "category_ids": [
            3661,
            2113,
            2106,
            1973
        ],
        "category_names": [
            "Bob",
            "Forside",
            "Ged",
            "Giraf"
        ],
        "description": "<p>...</p>",
        "list_description": "",
        "profile_name": "Default m. variant",
        "allow_negative_stock": 1,
        "brand_name": "TESTBRAND",
        "sale_price": 0,
        "real_price": 80,
        "price": 80,
        "price_dk": "80,00",
        "approx_shipping": 0,
        "delivery_time": "",
        "delivery_time_not_in_stock": "",
        "url": "https://example.com/produkt",
        "stock": 0,
        "attr_stock": null,
        "variant_stock": [
            {
                "attribute_id": 2740,
                "attribute_value_id": 16287,
                "name": "S",
                "weight": 10,
                "cnt": 4
            },
            {
                "attribute_id": 2740,
                "attribute_value_id": 16289,
                "name": "L",
                "weight": 30,
                "cnt": 9
            }
        ],
        "stock_string_da": "Nej",
        "avg_rating": null,
        "thumbnail": "https://example.com/cache/1/9/6/9/bob-fit-200x200x90.png",
        "meta_values": [
            null
        ],
        "online_since": 1651685711,
        "has_campaigns": true,
		"campaign_info": [
            {
                "name": "Bob",
                "price_model": "cheapest_free",
                "min_product_count": 4,
                "price": 0,
                "percent": 0,
                "created": "2023-02-01 14:55:47",
                "expires": null
            }
        ]
    }
]

For speed reasons, we cache the response from the ajax file, but in a test environment, you can add rebuild=1 as an argument to rebuild the page. We recommend not doing this in a production environment as it can significantly slow down the page.

The page /ajax_search contains a general search for products, categories, and landing pages. It can, for example, be used for autocomplete in your search field. It takes the following arguments:

term

Keyword

ajax_search?term=ostemad
limit

Maximum results

ajax_search?term=ostemad&limit=25
include

En kommasepareret liste af datatyper man ønsker at søge i. Værdierne kan være products, categories, blog_posts, pages, og landing_pages.

ajax_search?term=ostemad&include=products,categories
pretty

Set to 1 or 0 depending on whether the JSON response should be formatted nicely or not.

ajax_search?term=ostemad&pretty=1

Result is an array in this format:

[
    {
        "name": "Produkt et",
        "supplier": "",
        "description": "<p>...</p>",
        "price": 80,
        "sale_price": null,
        "normal_price": 80,
        "currency": "DKK",
        "price_dk": "80,00",
        "url": "https://example.com/produkt-et",
        "stock": 0,
        "stock_string_da": "Nej",
        "review_avg": 0,
        "thumbnail": "https://example.com/cache/1/9/8/4/box-100x100x90.png",
        "type": "product"
    },
    {
        "name": "Produkt to",
        "supplier": "",
        "description": "<p>...</p>",
        "price": 80,
        "sale_price": null,
        "normal_price": 80,
        "currency": "DKK",
        "price_dk": "80,00",
        "url": "https://example.com/produkt-to",
        "stock": 0,
        "stock_string_da": "Nej",
        "review_avg": 0,
        "thumbnail": "https://example.com/cache/1/9/6/9/bob-box-100x100x90.png",
        "type": "product"
    }
]