A document template is a typical document created with any text editor and saved as DOCX, DOC, PDF or ODT format. A template can contain fields and/or functions.
Our product uses those fields to insert data and generate documents that can be downloaded in DOCX or PDF format.
We recommend users to create templates using Microsoft Word, Office 365 or Google Docs and to save their templates as DOCX when using Word or Office 365 and as ODT when using Google Docs.
We support the inclusion of elements that simply match an element of data filled in by the user in the User Interface. Wherever an element occurs, our product substitutes the actual data value in the document. The inserted data inherits all the typesetting characteristics that are applied to the field, such as font and paragraph style.
The syntax for a simple field is:
<<element_name>>
Note
We discourage the use of hyphen -
in field names. If you choose to use one, you will need to surround the field name with brackets ([ ]
) when using it
into an expression, such as in Conditions, Date format, or Number format.
For example, <<[my-field-with-hyphens]>>
By default, the start of a field is annotated by <<
and the end of a field by >>
. So to create a field that will be replaced by the value of the field “firstname” for the employee, you would simply type <<firstname>>
into the document.
To be as unobtrusive as possible to the text of a template, our tool is strict about identifying fields and ignores
invalid mark-up, which is interpreted as plain text. For example, <<firstname>
is ignored and left as plain text because a closing >
character is missing. A single space between the <<
and the name or the name and >>
is allowed, but more also result in
the field not being recognized. The following table shows the typical types of error that result in a field not being recognized.
Example |
Valid |
Problem |
---|---|---|
|
Yes |
|
|
No |
Missing trailing |
|
No |
Missing leading |
|
No |
2 spaces before trailing |
|
No |
Space after leading |
Optional fields operate like the fields described earlier, except if there is no data for the value the
entire paragraph containing the field is removed.
Optional fields are specified with the prefix op:
, for example:
<<op:element_name>>
Optional fields are useful for condensing output (not leaving behind blank lines) when populating data. Consider a typical address block:
<<address1>>
<<address2>>
<<city>>, <<country>>
If you fill in no value for address2
, the output using the above sequence looks as follows:
53 Avenue Mirabeau
Paris, France
The blank line in the middle of the output above is likely to be undesirable and so using an optional field:
<<address1>>
<<op:address2>>
<<city>>, <<country>>
Results in the required output:
53 Avenue Mirabeau
Paris, France
Optional fields are also useful for removing paragraphs from numbered or bullet lists. Consider:
I have one <<item1>>
I have one <<item2>>
I have one <<item3>>
With data item2
= orange
and item3
= banana
, this results in:
I have one
I have one orange
I have one banana
Clearly item #1 above is incomplete because there is no data for it. Changing to optional paragraph fields resolves this:
I have one <<op:item1>>
I have one <<op:item2>>
I have one <<op:item3>>
With data item2
= orange
and item3
= banana
, this results in:
I have one orange
I have one banana
Note
Optional Fields strip the entire line or paragraph. Any additional content is also removed.
When using Smart Document Generation through our User Interface, you have the possibility to add predefined fields in your document template. These fields are divided into two types, standard fields which are known by our database without configuration on your side, and custom fields that you can create when configuring UKG HR Service Delivery platform.
If you insert a field in your template with the same name as one of these fields then, it is automatically detected.
Please note that the variable is replaced by the expected value only if the corresponding information is present in the employee folder.
<<firstname>>
<<lastname>>
<<external_id>>
<<email>>
<<status>
<<terminated>>
<<middlename>>
<<maidenname>>
<<language>>
<<birth_date>>
<<address1>>
<<address2>>
<<address3>>
<<zip_code>>
<<city>>
<<country>>
<<state>>
<<mobile_phone_number>>
<<starting_date>>
<<registration_references.employee_number>>
<<registration_references.active>>
(Note that this value is true
or false
depending on the selected employee number)
<<registration_references.departure_date>>
<<registration_references.organization_id>>
<<registration_references.organization_name>>
<<registration_references.organization_address1>>
<<registration_references.organization_address2>>
<<registration_references.organization_zip_code>>
<<registration_references.organization_city>>
<<registration_references.organization_country>>
<<hr_firstname>>
<<hr_lastname>>
<<hr_middlename>>
<<hr_email>>
<<hr_external_id>>
<<hr_language>>
<<hr_mobile_phone_number>>
You want to display the first name, middle name, last name and birth date of an employee,
by adding these four fields to your document template:
<<firstname>> <<lastname>> <<middlename>> <<birth_date>>
, and if the information is
present for the employee, the values are automatically filled when generating the document.
The syntax to insert a custom field in a template is the following:
<<custom_fields_code_of_my_custom_field>>
The code of the custom field that you have created is salary_amount
, then the field name is <<custom_fields_salary_amount>>
.
The code of the custom field that you have created is assignment-category
then the field name is <<custom_fields_assignment-category>>
.
Content blocks are parts of templates to be inserted into other templates. They are useful when you need to insert recurring content into several of your templates, such as logos and legal mentions.
To insert a content block in your template you need to add this field:
<<refLookup:my_block>>
Note
Here my_block
is the name of the reference to the content block but you can actually
call it whatever you want. When you upload your template in the User Interface, the
refLookup
syntax is detected and you are prompted to link this reference
to the content blocks of your choice.
Therefore, it is easier to create the content blocks before you create your templates.
Warning
refLookup
statement.Warning
refLookup
statement.<<refLookup:myfield007>>
: Correct<<refLookup:myfield>>
: Correct<<refLookup:my007field>>
: Correct<<refLookup:007myfield>>
: IncorrectConditional content is populated in the final document depending on the data filled in by the HR User in the User Interface. If, and only if, the specified condition is met, the content within the matching conditional section is rendered in the document.
Each conditional section is defined using a pair of fields: one to start the section and one to end it. The general syntax is:
<<cs_{my_condition}>>
<<es_>>
Note
cs
stands for “Conditional section” and es
for “End section”.
To provide else and else if options in a conditional section you can use the following syntax:
<<else_name>>
<<else_{expr}>>
<<else>>
The conditions can use expressions, variables and range specifiers, or any of the operators in this list:
Operator |
Description |
---|---|
|
Open / Close parenthesis (for math operations) |
|
Equals (for numbers and strings) |
|
Not equals (for numbers and strings) |
|
Addition (for numbers and strings) |
|
Subtraction |
|
Multiplication |
|
Division |
|
Modulus |
|
Less than (for numbers and strings) |
|
Less than or equals (for numbers and strings) |
|
Greater than (for numbers and strings) |
|
Greater than or equals (for numbers and strings) |
|
AND |
|
OR |
|
NOT (for boolean expressions) |
The conditional start and end tags are removed from the resulting document. If each tag is on a line by itself, the entire line is removed and the result is cleaner, this is why we recommend using those tags on dedicated lines when possible.
You can find an example template with conditions in the Example templates section.
Comments are sections of the templates that are ignored by document processing and never appear in the generated document. They are useful for:
Creating permanent notes in the template that are helpful to template authors and maintainers.
Disabling sections of templates temporarily to assist with development and maintenance.
Due to the two distinct requirements for comments above, there are times when you may need to comment out a section of a template which itself contains other comments. For example, you are temporarily disabling a section of the template which happens to contain comments that are permanent. To support this, we provide several distinct comment markup delimiters as shown by the following table. Different delimiters may be used to nest comments inside other comments.
Start delimiter |
End delimiter |
---|---|
|
|
|
|
As an example, a single comment may look as follows:
<</* The following section displays the person details if they exist */>>
<<cs_displayPersonDetails>>
And nested comments may looks as follows:
<<## This entire section is disabled
<</* This displays the salary of my employee */>>
<<salary>>
##>>
Dates can be inserted in the document template and formatted in the desired output format.
Warning
For this to work correctly, once the template is uploaded, the field must be edited and its type set to Date
.
The syntax for inserting a date in the template is:
<<{dateFormat(myDate)}>>
By default, if no desired output is specified the default output format is dd MMM yyyy (eg. 25 Nov 2021
).
If you want to format a date on a specific format, the syntax is:
<<{dateFormat(myDate, 'output-format')}>>
If you want to format a date according to the template language, the syntax is:
<<{dateFormat(my_date, 'EEEE MMMM yyyy', null, templateLocale)}>>
If the date selected for myDate
in the date picker was 25 November 2021
, the following table shows what different formats would produce:
Field |
Result |
---|---|
|
|
|
|
|
|
|
|
|
|
|
|
Note
$nowUTC
.<<{dateFormat($nowUTC,'dd/MM/yyyy')}>>
Code |
Date or Time Component |
Type |
Example |
---|---|---|---|
|
Era designator |
Text |
AD |
|
Year |
Year |
1996 |
|
Month in year (abbreviated number) |
Month |
7 |
|
Month in year (full number) |
Month |
07 |
|
Month in year (abbreviated) |
Month |
Jul |
|
Month in year (full) |
Month |
July |
|
Week in year |
Number |
27 |
|
Week in month |
Number |
2 |
|
Day in year |
Number |
189 |
|
Day in month |
Number |
10 |
|
Number of the day in a week |
Number |
2 |
|
Day of the week (abbreviated) |
Text |
Tue |
|
Day of the week (full) |
Text |
Tuesday |
|
AM/PM marker |
Text |
PM |
|
Hour in day (0-23) |
Number |
0 |
|
Hour in day (1-24) |
Number |
24 |
|
Hour in am/pm (0-11) |
Number |
0 |
|
Hour in am/pm (1-12) |
Number |
12 |
|
Minute in hour |
Number |
30 |
|
Seconds in minute |
Number |
55 |
|
Milliseconds |
Number |
978 |
|
Time zone |
General time zone |
PaciBc Standard Time; PST; GMT-08:00 |
|
Time zone |
RFC 822 time zone |
-800 |
You can also automatically translate a date in a specific language instead of using the format of the Document Generation template. The list of available codes is available below:
Code |
Country |
Example |
Result |
---|---|---|---|
ALB |
Albania |
|
27 shkurt 2024 |
ARE |
United Arab Emirates |
|
٢٧ فبراير ٢٠٢٤ |
ARG |
Argentina |
|
27 febrero 2024 |
AUS |
Australia |
|
27 February 2024 |
AUT |
Austria |
|
27 Februar 2024 |
BEL |
Belgium |
|
27 Februar 2024 |
BGR |
Bulgaria |
|
27 Февруари 2024 |
BHR |
Bahrain |
|
٢٧ فبراير ٢٠٢٤ |
BLR |
Belarus |
|
27 лютага 2024 |
BOL |
Bolivia |
|
27 febrero 2024 |
BRA |
Brazil |
|
27 febrero 2024 |
CAN |
Canada |
|
27 February 2024 |
CHE |
Switzerland |
|
27 Februar 2024 |
CHL |
Chile |
|
27 febrero 2024 |
CHN |
China |
|
27 二月 2024 |
COL |
Colombia |
|
27 febrero 2024 |
CRI |
Costa Rica |
|
27 febrero 2024 |
CZE |
Czech Republic |
|
27 února 2024 |
DEU |
Germany |
|
27 Februar 2024 |
DNK |
Denmark |
|
27 februar 2024 |
DOM |
Dominican Republic |
|
27 febrero 2024 |
DZA |
Algeria |
|
27 فيفري 2024 |
ECU |
Ecuador |
|
27 febrero 2024 |
EGY |
Egypt |
|
٢٧ فبراير ٢٠٢٤ |
ESP |
Spain |
|
27 de febreru 2024 |
EST |
Estonia |
|
27 veebruar 2024 |
FIN |
Finland |
|
27 February 2024 |
FRA |
France |
|
27 février 2024 |
GBR |
United Kingdom |
|
27 February 2024 |
GRC |
Greece |
|
27 Φεβρουαρίου 2024 |
GTM |
Guatemala |
|
27 febrero 2024 |
HKG |
Hong Kong |
|
27 February 2024 |
HND |
Honduras |
|
27 febrero 2024 |
HRV |
Croatia |
|
27 veljače 2024 |
HUN |
Hungary |
|
27 február 2024 |
IND |
India |
|
২৭ ফেব্ৰুৱাৰী ২০২৪ |
IRL |
Ireland |
|
27 February 2024 |
IRQ |
Iraq |
|
٢٧ شباط ٢٠٢٤ |
ISL |
Iceland |
|
27 febrúar 2024 |
ISR |
Israel |
|
٢٧ فبراير ٢٠٢٤ |
ITA |
Italy |
|
27 febbraio 2024 |
JOR |
Jordan |
|
27 شباط 2024 |
JPN |
Japan |
|
27 2月 2024 |
KOR |
South Korea |
|
27 2월 2024 |
KWT |
Kuwait |
|
٢٧ فبراير ٢٠٢٤ |
LBN |
Lebanon |
|
27 شباط 2024 |
LBY |
Libya |
|
27 فبراير 2024 |
LTU |
Lithuania |
|
27 vasaris 2024 |
LUX |
Luxembourg |
|
27 Februar 2024 |
LVA |
Latvia |
|
27 februāris 2024 |
MAR |
Morocco |
|
27 فبراير 2024 |
MEX |
Mexico |
|
27 febrero 2024 |
MKD |
Macedonia |
|
27 февруари 2024 |
NIC |
Nicaragua |
|
27 febrero 2024 |
NLD |
Netherlands |
|
27 February 2024 |
NOR |
Norway |
|
27 februar 2024 |
NZL |
New Zealand |
|
27 February 2024 |
OMN |
Oman |
|
٢٧ فبراير ٢٠٢٤ |
PAN |
Panama |
|
27 febrero 2024 |
PER |
Peru |
|
27 febrero 2024 |
POL |
Poland |
|
27 lutego 2024 |
PRI |
Puerto Rico |
|
27 February 2024 |
PRT |
Portugal |
|
27 Fevereiro 2024 |
PRY |
Paraguay |
|
27 febrero 2024 |
QAT |
Qatar |
|
٢٧ فبراير ٢٠٢٤ |
RUS |
Russia |
|
27 февраль 2024 |
SAU |
Saudi Arabia |
|
٢٧ فبراير ٢٠٢٤ |
SDN |
Sudan |
|
٢٧ فبراير ٢٠٢٤ |
SLV |
El Salvador |
|
27 febrero 2024 |
SVK |
Slovakia |
|
27 februára 2024 |
SVN |
Slovenia |
|
27 February 2024 |
SWE |
Sweden |
|
27 February 2024 |
SYR |
Syria |
|
27 شباط 2024 |
THA |
Thailand |
|
27 กุมภาพันธ์ 2567 |
TUN |
Tunisia |
|
27 فيفري 2024 |
TUR |
Turkey |
|
27 reşemiyê 2024 |
TWN |
Taiwan |
|
27 二月 2024 |
UKR |
Ukraine |
|
27 февраля 2024 |
URY |
Uruguay |
|
27 febrero 2024 |
USA |
United States |
|
27 February 2024 |
VEN |
Venezuela |
|
27 febrero 2024 |
YEM |
Yemen |
|
٢٧ فبراير ٢٠٢٤ |
ZAF |
South Africa |
|
27 Februarie 2024 |
To calculate the dates in your document (for example the end of a trial period or a contract end date), the syntax is:
<<{dateAdd(Date, amount, units)}>>
where:
Date
is name of the date field you want to add a time period to.
amount
is the number of days, months, years to add or remove.
units
is day
, month
or year
(singular or plural).
For example:
The expiration date of a trial period of 3 months from the starting date:
<<{dateAdd(starting_date, 3, 'months')}>>
adds 3 months to the starting date.
You can also calculate the difference between two dates in a given unit using the following syntax:
dateDiff(date1, date2, unit)
where:
date1
is the starting date.
date2
is the ending date.
unit
is day
, month
or year
(singular or plural).
For example:
<<{dateDiff('10 Jul 2020', '18 Jul 2020', 'days')}>>
results in 8
If you want to format a number on a specific format/pattern, the syntax is:
<<{numFormat(number, '### ###,##', 'FR', 'false')}>>
where ### ###,##
represents the pattern to display, and FR
is the optional locale that specifies the input format of number
.
<<{numFormat("1234,5", '#.###,#', 'DE')}>>
displays 1.234,5
<<{numFormat("1234.5", '#,###.#', 'US')}>>
displays 1,234.5
¤
character to the pattern to display the currency of the chosen locale:<<{numFormat("1234,5", '#.###,00¤', 'DE')}>>
displays 1.234,50€
Note
To write the pattern, non-breakable space character must be inserted (CTRL + Shift + Space on MS Word and LibreOffice) instead of a simple space.
Calculations can be performed in the generated document according the data filled in by the user in the User Interface. The result of a calculation can either be simply displayed in the generated document or used in conditions.
The syntax to create a calculation to be displayed in the generated document is:
<<{number1 + number2}>>
where +
can be replaced by any of the operators on this list:
Operator |
Description |
---|---|
|
Open / Close parenthesis (for math operations) |
|
Addition (for numbers and strings) |
|
Subtraction |
|
Multiplication |
|
Division |
|
Modulus |
Here is the syntax to store a calculation in a variable $myResult
, to be used later on in a condition:
<<$myResult={number1 + number2}>>
[...]
<<cs_{$myResult>10}>>
The text and elements of the conditional section.
<<es_>>
Several other functions can be used in a document template:
Function |
Description |
---|---|
|
Determines if the given element is null or empty.
isBlank(key) where:
-
key = the data valueExample:
<<{isBlank(name)}>> checks the value of name and returns true if it is null or empty, otherwise false .This can be useful for conditional sections:
<<cs_{isBlank(address)}>>
There is no address
<<es_>>
|
|
Uses a default value if the given element is null or empty.
ifBlank(key, default) where:
-
key = the data value-
default = the value to use if key is blankExample:
<<{ifBlank(name, 'Not Specified')}>> looks up
name in the data and returns Not Specified if it is null or empty. |
|
Maps one value to another.
map(key, test1, replace1 [,test2, replace2 ...] [,default]) where:
-
key = the data value-
test1 = the first value to compare with the key-
replace1 = the value to use if test1 matches the key-
test2 = the second value to compare with the key-
replace2 = the value to use if test2 matches the key(And so on)
-
default = the value to use if no matches are madeExample:
<<{map(gender, 'M', 'Male', 'F', 'Female', 'Other')}>> looks up
gender in the data and if it equals M the value Male is displayed.You can also use the
mapi function which is the same but case insensitive on the key and values. |
|
Replaces characters in the source string with new characters.
replace(string, oldChar, newChar) where:
string = the stringoldChar = the character to find in the stringnewChar = the character to use in place of the oldChar Example:
<<{replace(customerVIN, 'o', '0')}>> If the value of customerVIN is
JHMAB5227EC8oo65o ,then the replace function replaces all the
o characters to the number 0 .The result is
JHMAB5227EC800650 . |
|
Replaces strings in the source string with the new string.
replaceStr(string, searchFor, replaceWith [, ignoreCase]) where:
-
string = the string-
searchFor = the characters to find in the string-
newChar = the characters to use in place of searchFor -
ignoreCase = true | false to ignore the case when searching. Defaults to false (ie case-sensitive)Example:
<<{replaceStr(address, 'street', 'St.', true)}>> If
address = Matheson street , the function returns Matheson St. Note that this function returns every occurrence of the found string.
If you want to replace only the first occurrence, use the
replaceFirst function instead. |
|
Replaces all double quote characters in the given string with single quotes.
All forms of double quotes are replaced. This is handy since the templates use single quotes for delimiters.
squote(string) where:
-
string = the string in which to replace double quotesExample:
<<{squote('This is Amy"s.')}>> returns This is Amy's. |
|
Splits a string into parts that can be displayed separately.
split(string, splitChar, index) where:
-
string = the string to split-
splitChar = the character to use as a delimiter-
index = once split into parts, index identifies the part to be used – counting from 0 Examples:
<<{split('John|Mathews|47|Approved' , '|' , 1)}>> returns
Mathews <<{split(cityStateZIPCountry , ';' , 3)}>> if
cityStateZIPCountry = Charleston;West Virginia;29402;United States then the function returns
United States . |
|
Displays a subsection of a string given starting and finishing indexes.
substring(string, start, finish) where:
-
string = the string-
strat = the position in the string that becomes the first character. Indexing starts at 0 .-
finish = the position in the string that marks where to cut the string; the character before the cut makes it into the substring, the ending character doesn’t.Examples:
<<{substring('0123456' , 2 , 5)}>> returns 234 <<{substring(LatLong , 0 , 6)}>> with
LatLong = 31.9088983S115.8049265E returns 31.908 |
|
Removes leading and trailing spaces from a string.
trim(string) where:
-
string = the stringExample:
<<{trim(productID)}>> where
productID = " 12CVCV123-454 " returns
12CVCV123-454 |
|
Returns the character at the requested position in the source string.
charAt(string, position) where:
-
string = the string to lookup the character in-
position = the position of the required character, starting from 0 for the first positionExamples:
<<{charAt('abcdefg', 3)}>> returns the character d .<<{charAt(myNumber, 6)}>> looks up myNumber in the data.If
myNumber = ID474-K234 then the function returns K . |
|
Checks to see if a string ends with a given string.
endsWith(mainString, subString) where:
-
mainString = the string to check-
subString = the string to look for at the end of mainString Example:
<<{endsWith('I am a potato', 'tato')}>> returns true .Useful when creating a conditional section. Here, the
serialNum field is only displayed if it ends with ZZZ :<<cs_{endsWith(serialNum, 'ZZZ')}>>
<<serialNum>>
<<es_>>
The
startsWith function does the same thing for the beginning of a string. |
|
Compares two strings, regardless of case.
equalsIgnoreCase(string1, string2) where:
-
string1 = the first string-
string2 = the second to compare to the first stringExample:
<<{equalsIgnoreCase ('i am Bob', 'I am bob')}>> returns true . |
|
Returns the length of a string.
length(string) where:
-
string = the string which length must be checkedExample:
<<{length('Bob')}>> returns the number 3.0 .Can be used when creating a conditional section. Here, the section is only displayed if
refNo is set:<<cs_{length(refNo) > 0}>>
Ref Num : <<refNo>>
<<es_>>
However, we recommend using the
isBlank function for this specific use case. |
|
Changes the string so that the first character of each word is a capital letter.
titleCase(string) where:
-
string = the string to convertExample:
<<{titleCase ('bob mathews')}>> returns
Bob Mathews .<<{titleCase (firstname+ ' ' + lastname)}>> with
firstname = bob and lastname = MATHEWS also returns
Bob Mathews . |
|
Adjusts the given string converting it to sentence case.
toSentence(string) where:
-
string = the string to convertExample:
<<{toSentence('a little. ditty')}>> returns
A little. Ditty . |
|
Returns the string using all lower case characters.
toLowerCase(string) where:
-
string = the string to convertExample:
<<{toLowerCase('Bob Mathews')}>> returns
bob mathews .The
toUpperCase function does the same thing with upper case characters. |
|
Returns the starting index of one string inside another.
Note that the first index is
0 .indexOf(string, find [, startIdx]) where:
-
string = the string to scan-
find = the string to find-
startIdx = an optional search starting indexExample:
<<{indexOf('Bob Mathews', 'Mat')}>> returns
4.0 . |
|
Converts a given number to a letter in the following sequence:
a, b, c, … z, aa, bb, cc, … zz, aaa, bbb, etc.
toAlpha(key) where:
-
key = the data valueExample:
<<{toAlpha(index)}>> when index =
3 , the function returns c .when index =
28 , the function returns bb .The function
toAlpha2 does the same thing with the sequence:a, b, c, … z, aa, ab, ac … az, ba, bb, etc.
|
|
Converts a given number to a roman numeral:
toRoman(key) where:
-
key = the data valueExample:
<<{toRoman(index)}>> when index =
2 , the function returns ii .when index =
29 , the function returns xxix . |
|
Respectively returns the smaller or larger of two numbers.
min(number1, number2) and max(number1, number2) Examples:
<<{min(53.5,23.1)}>> returns 23.1 <<{max(53.5,23.1)}>> returns 53.5 |
|
Respectively returns the next smaller or larger whole number.
floor(number) and ceil(number) Examples:
<<{floor(153.57)}>> returns 153.0 <<{ceil(153.57)}>> returns 154.0 |
|
Returns the absolute value of a number.
abs(number) Examples:
<<{abs(-153.57)}>> returns 153.57 <<{abs(temp)}>> with temp = -273.15 , returns 273.15 |
|
Returns the power of two numbers.
pow(number1, number2) Example:
<<{pow(7,2)}>> returns 49.0 |
|
Returns the square root of a number.
sqrt(number) Example:
<<{sqrt(81.0)}>> returns 9.0 |
|
Rounds the number to the specified number of places.
round(number [, places]) where:
-
number = the number to round-
places = the number of decimal places required (defaults to zero)Examples:
<<{round(153.75)}>> returns 154 <<{round(153.73455,2)}>> returns 153.73 |
|
Returns a random number between 0 and 1.
random() Example:
<<{round(random()*100)}>> returns a random number between 0 and 100 . |
You can download these templates to avoid starting from scratch: