module built-in
// hibbernate transaction management

native class utils.ValidationException as NativeValidationException {
getName() : String
getErrorMessage() : String
isRelevantObject(Object) : Bool
}

native class utils.HibernateTransactionHelper as HibernateTransactionHelper {
static commitAndStartNewTransaction() : List<NativeValidationException>
static rollbackAndStartNewTransaction()
}

function commitAndStartNewTransaction() : List<NativeValidationException> {
return HibernateTransactionHelper.commitAndStartNewTransaction();
}

function rollbackAndStartNewTransaction() {
HibernateTransactionHelper.rollbackAndStartNewTransaction();
}
// section session management

invoke internalCleanupSessionManagerEntities() every 10 minutes

function internalUpdateSessionManagerTimeout(){
var n : DateTime := now().addMinutes(-30); // update lastUse after 30 minutes to avoid unnecessary db writes, also sets minimum timeout to 30 minutes
var man := getSessionManager();
if(man.lastUse == null || man.lastUse.before(n)){
man.lastUse := now();
}
}

function internalCleanupSessionManagerEntities(){
var sessiontimeout := 10000; //minutes
var n : DateTime := now().addMinutes(-1*(sessiontimeout));
var ses := from SessionManager as sc where sc.lastUse is null or sc.lastUse < ~n limit 25; //use limit to avoid loading all obsolete entities in one transaction
for(s : SessionManager in ses){
s.delete();
}
}

// section search

//optimization of search index, twice a day
invoke optimizeSearchIndex() every 12 hours
//Update the spell check and autocompletion indices twice a day
invoke updateSuggestionIndex() every 12 hours
//renew facet index readers every 15 minutes
invoke renewFacetIndexReaders() every 15 minutes

function renewFacetIndexReaders(){
IndexManager.renewFacetIndexReaders();
}

function optimizeSearchIndex(){
IndexManager.optimizeIndex();
}

function updateSuggestionIndex(){
IndexManager.indexSuggestions();
}

native class webdsl.generated.search.IndexManager as IndexManager {
static indexSuggestions()
static indexSuggestions(List<String>)
static optimizeIndex()
static renewFacetIndexReaders()
static clearAutoCompleteIndex(String)
static clearSpellCheckIndex(String)
static reindex(Entity)
static removeFromIndex(Entity)
static reindex()
}
native class org.webdsl.search.SearchHelper as SearchHelper {
static firstIndexLink(Int, Int, Int): Int
static lastIndexLink(Int, Int, Int): Int
}

native class org.webdsl.search.DynamicSearchField as DynamicSearchField {
constructor(String, String)
}

native class org.webdsl.search.WebDSLFacet as Facet {
constructor()
isSelected() : Bool
getCount() : Int
getValue() : String
getFieldName() : String
getValueAsDate() : Date
getValueAsFloat() : Float
getValueAsInt() : Int
must() : Facet
mustNot() : Facet
should() : Facet
isMust() : Bool
isMustNot() : Bool
isShould() : Bool
}

native class org.webdsl.search.AbstractEntitySearcher as Searcher {
static escapeQuery(String) : String
static fromString(String) : Searcher
asString() : String
enableFaceting(String,Int) : Searcher
enableFaceting(String,String) : Searcher
getFacets(String) : List<Facet>
addFacetSelection(Facet) : Searcher
addFacetSelection(List<Facet>) : Searcher
getFacetSelection() : List<Facet>
getFacetSelection(String) : List<Facet>
removeFacetSelection(Facet) : Searcher
clearFacetSelection() : Searcher
clearFacetSelection(String) : Searcher
highlight(String, String) : String
highlight(String,String,String,String,Int,Int,String) : String
highlight(String,String,String,String) : String
highlightLargeText(String, String) : String
highlightLargeText(String,String,String,String,Int,Int,String) : String
highlightLargeText(String,String,String,String) : String
highlightHTML(String, String) : String
highlightHTML(String,String,String,String,Int,Int,String) : String
highlightHTML(String,String,String,String) : String
highlightLargeHTML(String, String) : String
highlightLargeHTML(String,String,String,String,Int,Int,String) : String
highlightLargeHTML(String,String,String,String) : String
getQuery() : String
luceneQuery() : String
searchTime() : String
searchTimeMillis() : Int
searchTimeSeconds() : Float
allowLuceneSyntax(Bool) : Searcher
addFieldFilter(String,String) : Searcher
getFilteredFields() : List<String>
getFieldFilterValue(String) : String
removeFieldFilter(String) : Searcher
clearFieldFilters() : Searcher
startMustClause() : Searcher
startMustNotClause() : Searcher
startShouldClause() : Searcher
must() : Searcher
should() : Searcher
not() : Searcher
endClause() : Searcher
setNamespace(String) : Searcher
getNamespace() : String
removeNamespace() : Searcher
boost(String,Float) : Searcher
defaultAnd() : Searcher
defaultOr() : Searcher
field(String) : Searcher
fields(List<String>) : Searcher
defaultFields() : Searcher
getFields() : List<String>
setOffset(Int) : Searcher
getOffset() : Int
setLimit(Int) : Searcher
getLimit() : Int
scores() : List<Float>
explanations() : List<String>
count() : Int
moreLikeThis(String) : Searcher
sortDesc(String) : Searcher
sortAsc(String) : Searcher
clearSorting() : Searcher
reset() : Searcher
query(String) : Searcher
phraseQuery(String,Int) : Searcher
regexQuery(String) : Searcher
rangeQuery(Int,Int,Bool,Bool) : Searcher
rangeQuery(Float,Float,Bool,Bool) : Searcher
rangeQuery(Date,Date,Bool,Bool) : Searcher
rangeQuery(String,String,Bool,Bool) : Searcher
matchAllQuery() : Searcher
}

native class org.webdsl.search.SearchStatistics as SearchStatistics{
static clear() : Void
static getSearchQueryExecutionCount() : Long
static getSearchQueryTotalTime() : Long
static getSearchQueryExecutionMaxTime() : Long
static getSearchQueryExecutionAvgTime() : Long
static getSearchQueryExecutionMaxTimeQueryString() : String
static getObjectLoadingTotalTime() : Long
static getObjectLoadingExecutionMaxTime() : Long
static getObjectLoadingExecutionAvgTime() : Long
static getObjectsLoadedCount() : Long
static isStatisticsEnabled() : Bool
static setStatisticsEnabled(Bool) : Bool
static getSearchVersion() : String
static getIndexedClassNames() : List<String>
static indexedEntitiesCount() : List<String>
}

//The default analyzer, equal to the one used by default in hibernate search
default_builtin_analyzer analyzer hsearchstandardanalyzer {
tokenizer = StandardTokenizer
token filter = StandardFilter
token filter = LowerCaseFilter
token filter = StopFilter
}

//Template showing the info available through Hibernate Search statistics
define showSearchStats(){
var NStoMS : Long := 1000000L;
table{
row { column { <b>"Search statistics"</b> } }
row { column { "Statistics enabled?" } column { output(SearchStatistics.isStatisticsEnabled()) if( !SearchStatistics.isStatisticsEnabled() ){ "(Enabled through searchstats=true in application.ini)" } } }
row { column { "Hibernate Search version" } column { output(SearchStatistics.getSearchVersion()) } }

row { column { <b>"Query execution times"</b> } }

row { column { "Search query execution count" } column { output(SearchStatistics.getSearchQueryExecutionCount()) } }
row { column { "Total search time" } column { output(SearchStatistics.getSearchQueryTotalTime()/NStoMS ) "ms (" output(SearchStatistics.getSearchQueryTotalTime())"ns)" } }
row { column { "Average search query exec time" } column { output(SearchStatistics.getSearchQueryExecutionAvgTime()/NStoMS ) "ms (" output(SearchStatistics.getSearchQueryExecutionAvgTime())"ns)" } }
row { column { "Slowest search query exec time" } column { output(SearchStatistics.getSearchQueryExecutionMaxTime()/NStoMS ) "ms (" output(SearchStatistics.getSearchQueryExecutionMaxTime())"ns)" } }
row { column { "Slowest search query" } column { output(SearchStatistics.getSearchQueryExecutionMaxTimeQueryString()) } }

row { column { <b>"Object load times"</b> } }

row { column { "Objects loaded count" } column {output(SearchStatistics.getObjectsLoadedCount()) } }
row { column { "Total object loading time" } column { output(SearchStatistics.getObjectLoadingTotalTime()/ NStoMS ) "ms (" output(SearchStatistics.getObjectLoadingTotalTime())"ns)" } }
row { column { "Average object loading time" } column { output(SearchStatistics.getObjectLoadingExecutionAvgTime()/NStoMS ) "ms (" output(SearchStatistics.getObjectLoadingExecutionAvgTime())"ns)" } }
row { column { "Slowest object loading time" } column { output(SearchStatistics.getObjectLoadingExecutionMaxTime()/NStoMS ) "ms (" output(SearchStatistics.getObjectLoadingExecutionMaxTime())"ns)" } }
} table {
row { column { <b>"Indexed entities (entity - nOfEntities)"</b> } }
row { column { output(SearchStatistics.indexedEntitiesCount()) } }
}
}

//tries to highlight the elements inside, not touching the html tags inside (highlighter invoked to ignore html tags)
//If nothing is highlighted, it just renders elements
define highlight(s : Searcher, fld : String){
var rendered := rendertemplate( elements );
var renderedHL := if(s != null) s.highlightLargeHTML(fld, rendered, "<span class=\"highlightContent\">", "</span>", 1, 10000000, "") else "";
if(renderedHL != null && renderedHL.length() > 0) {
rawoutput( renderedHL )
} else {
rawoutput( rendered ) //don't render twice
}
}

// section methods for built-in types

type String { //includes other String-based types such as Secret, Patch, Email, URL, etc.
length():Int
toLowerCase():String
toUpperCase():String
replace(String,String):String
startsWith(String):Bool
startsWith(String,Int):Bool
endsWith(String):Bool
trim():String
utils.StringType.parseUUID as parseUUID():UUID
org.webdsl.tools.Utils.containsDigit as containsDigit():Bool
org.webdsl.tools.Utils.containsLowerCase as containsLowerCase():Bool
org.webdsl.tools.Utils.containsUpperCase as containsUpperCase():Bool
org.webdsl.tools.Utils.isCleanUrl as isCleanUrl():Bool
org.apache.commons.lang3.StringUtils.contains as contains(String):Bool // this 'contains' function handles null, null as either arg will produce false
utils.StringType.parseInt as parseInt():Int
utils.StringType.split as split():List<String>
utils.StringType.splitWithSeparator as split(String):List<String> //TODO Regex as argument
utils.StringType.parseLong as parseLong():Long
utils.StringType.parseFloat as parseFloat():Float
utils.DateType.parseDate as parseDate(String):Date
utils.DateType.parseDate as parseDateTime(String):DateTime
utils.DateType.parseDate as parseTime(String):Time
org.apache.commons.lang3.StringEscapeUtils.escapeEcmaScript as escapeJavaScript():String
substring(Int):String
substring(Int,Int):String
}

type Secret {
org.webdsl.tools.Utils.secretDigest as digest():Secret
org.webdsl.tools.Utils.secretCheck as check(Secret):Bool
}

type Patch {
name.fraser.neil.plaintext.patch_factory.patchApply as applyPatch(String):String
}
type String {
name.fraser.neil.plaintext.patch_factory.patchMake as makePatch(String):Patch
name.fraser.neil.plaintext.patch_factory.diff as diff(String):List<String>
}

type DateTime { // includes Date and Time types
utils.DateType.format as format(String):String
before(DateTime):Bool
after(DateTime):Bool
getTime():Long
setTime(Long)
utils.DateType.addYears as addYears(Int):DateTime
utils.DateType.addMonths as addMonths(Int):DateTime
utils.DateType.addDays as addDays(Int):DateTime
utils.DateType.addHours as addHours(Int):DateTime
utils.DateType.addMinutes as addMinutes(Int):DateTime
utils.DateType.addSeconds as addSeconds(Int):DateTime
utils.DateType.getYear as getYear():Int
utils.DateType.getMonth as getMonth():Int
utils.DateType.getDay as getDay():Int
utils.DateType.getDayOfYear as getDayOfYear():Int
utils.DateType.getHour as getHour():Int
utils.DateType.getMinute as getMinute():Int
utils.DateType.getSecond as getSecond():Int
}

function age(d:Date):Int{
var today : Date := today();
var age := today.getYear() - d.getYear();
if(today.getDayOfYear() < d.getDayOfYear()){
age := age - 1;
}
return age;
}

native class utils.DateType as DateType{ //@TODO static functions not yet supported in type import of DateTime above
static getDefaultDateFormat():String
static getDefaultTimeFormat():String
static getDefaultDateTimeFormat():String
}

type WikiText{
org.webdsl.tools.WikiFormatter.wikiFormat as format():String
}

type Email {
utils.EmailType.isValid as isValid():Bool
}

type URL {
utils.URLType.isValid as isValid():Bool
}

type File{
getContentAsString():String
getContentType():String
setContentType(String)
}
type Image{
getContentAsString():String
getContentType():String
setContentType(String)
}

// access to servlet context

native class AbstractDispatchServletHelper as DispatchServlet {
getIncomingSuccessMessages():List<String>
clearIncomingSuccessMessages()
static get(): DispatchServlet
}
function getDispatchServlet():DispatchServlet{
return DispatchServlet.get();
}

// access to page context

native class AbstractPageServlet as PageServlet {
inSubmittedForm() : Bool
formRequiresMultipartEnc : Bool
getFileUpload(String) : File
getLabelString() : String
inLabelContext() : Bool
addValidationException(String,String)
getValidationErrorsByName(String):List<String>
static getRequestedPage() : PageServlet
enterLabelContext(String)
leaveLabelContext()
setTemplateContext(TemplateContext)
getTemplateContext():TemplateContext
setMimetype(String)
isRedirected():Bool
getRedirectUrl():String
setRedirectUrl(String)
enableDownloadInline()
}
function getPage():PageServlet{
return PageServlet.getRequestedPage();
}

native class utils.TemplateContext as TemplateContext {
clone():TemplateContext
getTemplateContextString():String
}

function mimetype(s:String){
getPage().setMimetype(s);
}
template mimetype(s:String){
init{
getPage().setMimetype(s);
}
}

function downloadInline(){
getPage().enableDownloadInline();
}
template downloadInline(){
init{
downloadInline();
}
}

//access to template context

native class TemplateServlet as TemplateServlet {
getUniqueId() : String
static getCurrentTemplate() : TemplateServlet
}
function getTemplate() : TemplateServlet{
return TemplateServlet.getCurrentTemplate();
}

// utitity for templates that handle validation

function handleValidationErrors(errors : List<String>): List<String>{
var result :List<String> := null;
if(errors != null && errors.length > 0){
if(getPage().inLabelContext()){
for(s:String in errors){
getPage().addValidationException(getPage().getLabelString(),s);
}
}
else{
result := errors;
}
cancel();
}
return result;
}

// section JSON for services

native class org.json.JSONObject as JSONObject {
constructor()
constructor(String)
NULL : Object
get(String) : Object
getBoolean(String) : Bool
getDouble(String) : Double
getInt(String) : Int
getLong(String) : Long
getJSONArray(String) : JSONArray
getJSONObject(String) : JSONObject
getString(String) : String
has(String) : Bool
names() : JSONArray
put(String, Object)
toString() : String
toString(Int) : String
}

native class org.json.JSONArray as JSONArray {
constructor()
constructor(String)
get(Int) : Object
getBoolean(Int) : Bool
getDouble(Int) : Double
getInt(Int) : Int
getJSONArray(Int) : JSONArray
getJSONObject(Int) : JSONObject
getString(Int) : String
length() : Int
join(String) : String
put(Object)
remove(Int)
toString() : String
toString(Int) : String
}

native class java.lang.Double as Double {
constructor(Double)
constructor(String)
floatValue() : Float

}
// section WebDriver for testing

function sleep(i:Int){ UtilsTestClass.sleep(i); }
function createTempFile(s:String):String{ return UtilsTestClass.createTempFile(s); }

function getFirefoxDriver():FirefoxDriver{ return UtilsTestClass.getFirefoxDriver(); }
function getHtmlUnitDriver():HtmlUnitDriver{ return UtilsTestClass.getHtmlUnitDriver(); }
function getDriver():WebDriver{ return getFirefoxDriver(); }

native class utils.Test as UtilsTestClass {
static sleep(Int)
static getHtmlUnitDriver():HtmlUnitDriver
static getFirefoxDriver():FirefoxDriver
static closeDrivers()
static createTempFile(String):String
}

native class org.openqa.selenium.WebDriver as WebDriver {
get(String)
getTitle():String
getPageSource():String
findElement(SelectBy):WebElement
findElements(SelectBy):List<WebElement>
close()
utils.Test.runJavaScript as runJavaScript(String):String
}

native class org.openqa.selenium.By as SelectBy {
static className(String):SelectBy
static id(String):SelectBy
static linkText(String):SelectBy
static name(String):SelectBy
static partialLinkText(String):SelectBy
static tagName(String):SelectBy
static cssSelector(String):SelectBy
static xpath(String):SelectBy
}

native class org.openqa.selenium.WebElement as WebElement {
getText():String
utils.Test.getValue as getValue() : String //WebElement.getValue() is deprecated
getElementName():String
getAttribute(String):String
isEnabled():Bool
sendKeys(String)
submit()
clear()
isEnabled():Bool
isSelected():Bool
findElements(SelectBy):List<WebElement>
//void sendKeys(java.lang.CharSequence... keysToSend)
utils.Test.click as toggle()
utils.Test.click as setSelected()
utils.Test.clickAndWait as click()
}

native class org.openqa.selenium.htmlunit.HtmlUnitDriver as HtmlUnitDriver : WebDriver {
constructor()
}

native class org.openqa.selenium.firefox.FirefoxDriver as FirefoxDriver : WebDriver {
constructor()
}

native class org.openqa.selenium.support.ui.Select as Select {
deselectAll() // Clear all selected entries.
deselectByIndex(Int) // Deselect the option at the given index.
deselectByValue(String) // Deselect all options that have a value matching the argument.
deselectByVisibleText(String) // Deselect all options that display text matching the argument.
escapeQuotes(String):String
getAllSelectedOptions():List<WebElement>
getFirstSelectedOption():WebElement
getOptions():List<WebElement>
isMultiple():Bool
selectByIndex(Int) // Select the option at the given index.
selectByValue(String) // Select all options that have a value matching the argument.
selectByVisibleText(String) // Select all options that display text matching the argument.
constructor(WebElement)
}

//email

entity QueuedEmail {
body :: String (length=1000000) //Note: default length for string is currently 255
to :: String (length=1000000)
cc :: String (length=1000000)
bcc :: String (length=1000000)
replyTo :: String (length=1000000)
from :: String (length=1000000)
subject :: String (length=1000000)
scheduled :: DateTime (default=now())
lastTry :: DateTime
}

invoke internalHandleEmailQueue() every 30 seconds

function internalHandleEmailQueue(){
var n : DateTime := now().addHours(-3); // retry after 3 hours to avoid spamming too much
var queuedEmails := from QueuedEmail as q where q.lastTry is null or q.lastTry < ~n order by q.scheduled asc limit 1;

for(queuedEmail:QueuedEmail in queuedEmails){
if(sendemail(sendQueuedEmail(queuedEmail))){
queuedEmail.delete();
}
else{
queuedEmail.lastTry := now();
}

//normally you would use email(sendQueuedEmail(queuedEmail)) to send email, however,
//that is desugared to renderemail(queuedEmail).save() to make it asynchronous.
//In this function the email is actually send, using the synchronous sendemail function.
}
}

native class utils.ThreadLocalEmailContext as ThreadLocalEmailContext {
static inEmailContext() : Bool
}

function inEmailContext() : Bool {
return ThreadLocalEmailContext.inEmailContext();
}
define email sendQueuedEmail(q:QueuedEmail){
to(q.to)
from(q.from)
subject(q.subject)
cc(q.cc)
bcc(q.bcc)
replyTo(q.replyTo)
rawoutput{ //don't escape the html from internal email rendering
output(q.body)
}
}


// logging

entity RequestLogEntry {
name :: String
requestedURL :: Text
start :: DateTime
end :: DateTime
clientIP :: String
clientPort :: Int
method :: String
referer :: Text
userAgent :: Text
}

//built-in templates

define ignore-access-control break(){
<br all attributes/>
}
/*
define ignore-access-control block(){
<div class="block "+attribute("class")
all attributes except "class">
elements()
</div>
}*/

define ignore-access-control div(){
<div all attributes>
elements()
</div>
}

define ignore-access-control container(){
<span class="container "+attribute("class")
all attributes except "class">
elements()
</span>
}

define ignore-access-control fieldset(s:String){
<fieldset all attributes>
<legend>
output(s)
</legend>
elements()
</fieldset>
}

define ignore-access-control group(s:String){
<fieldset all attributes>
<legend>
output(s)
</legend>
<table>
elements()
</table>
</fieldset>
}

define ignore-access-control group(){
<fieldset class="fieldset_no_legend_ "+attribute("class")
all attributes except "class">
<table>
elements()
</table>
</fieldset>
}

define ignore-access-control groupitem(){
<tr all attributes>
elements()
</tr>
}

define ignore-access-control table(){
<table all attributes>
elements()
</table>
}

define ignore-access-control row(){
<tr all attributes>
elements()
</tr>
}

define ignore-access-control column(){
<td all attributes>
elements()
</td>
}

define ignore-access-control list(){
<ul all attributes>
elements()
</ul>
}

define ignore-access-control listitem(){
<li all attributes>
elements()
</li>
}

define ignore-access-control par(){
<p all attributes>
elements()
</p>
}

define ignore-access-control pre(){
<pre all attributes>
elements()
</pre>
}

define ignore-access-control spacer(){
<hr all attributes/>
}

/*
menubar{
menu
{
menuheader{ ... }
menuitems{
menuitem{ ... }
menuitem{ ... }
}
}
}
*/

define ignore-access-control menubar(){
var elementid := "menu"+getTemplate().getUniqueId()
includeCSS("dropdownmenu.css")
<div class="menuwrapper" id=elementid all attributes>
<ul id="p7menubar" class="menubar">
elements()
</ul>
</div>
}

define ignore-access-control menuspacer(){
<li all attributes>
elements()
</li>
}

define ignore-access-control menu(){
<li class="menu" all attributes>
elements()
</li>
}

define ignore-access-control menuheader(){
<span class="menuheader" all attributes>
elements()
</span>
}

define ignore-access-control menuitems(){
<ul class="menuitems">
elements()
</ul>
}

define ignore-access-control menuitem(){
<li class="menuitem" all attributes>
elements()
</li>
}

//reflection of entities

native class org.webdsl.lang.ReflectionEntity as ReflectionEntity{
getName():String
getProperties():List<ReflectionProperty>
getPropertyByName(String):ReflectionProperty
hasViewPage():Bool
static byName(String):ReflectionEntity
static all():List<ReflectionEntity>
}

native class org.webdsl.lang.ReflectionProperty as ReflectionProperty{
getName() : String
hasNotNullAnnotation() : Bool
getFormatAnnotation() : String
}

//validation wrapper for submit and submitlink

define ignore-access-control wrapsubmit(tname:String) requires s(String){
if(getValidationErrorsByName(tname).length > 0){
errorTemplateAction(getValidationErrorsByName(tname)){
s(tname)
}
}
else{
s(tname)
}
}

//reused when elements() are empty

define ignore-access-control elementsempty(){}

// Date/DateTime/Time input and output templates

define ignore-access-control output(d:Ref<DateTime>){
var default := DateType.getDefaultDateTimeFormat();
dateoutputgeneric(d as Ref<Date>, default)[all attributes]
}

define ignore-access-control output(d:Ref<Time>){
var default := DateType.getDefaultTimeFormat();
dateoutputgeneric(d as Ref<Date>, default)[all attributes]
}

define ignore-access-control output(d:Ref<Date>){
var default := DateType.getDefaultDateFormat();
dateoutputgeneric(d,default)[all attributes]
}

define ignore-access-control dateoutputgeneric(d:Ref<Date>, defaultformat : String){
var dateformat := defaultformat;
init{
//@TODO add support for ref arg in function, to avoid repeating this in both output and input
var attr := attribute("format");
if(attr!=null && attr != ""){
dateformat := attr;
}
else{
if(d.getReflectionProperty() != null){
var formatanno := d.getReflectionProperty().getFormatAnnotation();
if(formatanno!=null){
dateformat := formatanno;
}
}
}
}
output(d.format(dateformat))
}

define ignore-access-control input(d:Ref<DateTime>){
var format := DateType.getDefaultDateTimeFormat()
var dateformatString := ""
var timeformatString := ""
init{
var attr := attribute("format");
if(attr!=null && attr != ""){
format := attr;
}
else{
if(d.getReflectionProperty() != null){
var formatanno := d.getReflectionProperty().getFormatAnnotation();
if(formatanno!=null){
format := formatanno;
}
}
}
var tmp := format.split(" "); //assumes date and time are separated by space and no other spaces in the format string
dateformatString := "dateFormat: '"+convertDateFormatToJQuery(tmp[0])+"', ";
timeformatString := "timeFormat: '"+convertTimeFormatToJQuery(tmp[1])+"', ";
}
dateinputgeneric(d as Ref<Date>, format, "datetimepicker", dateformatString+timeformatString+" changeMonth: true, changeYear: true, yearRange: '1900:new Date().getFullYear()'")[all attributes]{elements()}
}

define ignore-access-control input(d:Ref<Time>){
var format := DateType.getDefaultTimeFormat()
var timeformatString := ""
init{
var attr := attribute("format");
if(attr!=null && attr != ""){
format := attr;
}
else{
if(d.getReflectionProperty() != null){
var formatanno := d.getReflectionProperty().getFormatAnnotation();
if(formatanno!=null){
format := formatanno;
}
}
}
timeformatString := "timeFormat: '"+convertTimeFormatToJQuery(format)+"'";
}
dateinputgeneric(d as Ref<Date>, format, "timepicker", timeformatString)[all attributes]{elements()}
}

function convertTimeFormatToJQuery(f:String):String{
return f.replace("H","h");
}
function convertDateFormatToJQuery(f:String):String{
return f.replace("yyyy","yy").replace("MM","mm");
}

define ignore-access-control input(d:Ref<Date>){
var format := DateType.getDefaultDateFormat()
var dateformatString := ""
init{
var attr := attribute("format");
if(attr!=null && attr != ""){
format := attr;
}
else{
if(d.getReflectionProperty() != null){
var formatanno := d.getReflectionProperty().getFormatAnnotation();
if(formatanno!=null){
format := formatanno;
}
}
}
dateformatString := "dateFormat: '"+convertDateFormatToJQuery(format)+"', ";
}
dateinputgeneric(d, format, "datepicker", dateformatString+" changeMonth: true, changeYear: true, yearRange: '1900:new Date().getFullYear()'")[all attributes]{elements()}
}

define ignore-access-control dateinputgeneric(d:Ref<Date>, dateformat : String, picker : String, options:String){
var tname := getTemplate().getUniqueId()
var req := getRequestParameter(tname)

request var errors : List<String> := null

if(errors != null && errors.length > 0){
errorTemplateInput(errors){
datepickerinput(d,dateformat,tname,picker,options)[all attributes]
}
validate{ getPage().enterLabelContext(tname); }
elements()
validate{ getPage().leaveLabelContext();}
}
else{
datepickerinput(d,dateformat,tname,picker,options)[all attributes]
validate{ getPage().enterLabelContext(tname); }
elements()
validate{ getPage().leaveLabelContext();}
}
validate{
if(req != null){
if(req!="" && req.parseDate(dateformat)==null){
errors := ["Incorrect date format, expected format is "+dateformat];
}
}
if(errors == null){ // if no wellformedness errors, check datamodel validations
errors := d.getValidationErrors();
errors.addAll(getPage().getValidationErrorsByName(tname)); //nested validate elements
}
errors := handleValidationErrors(errors);
}
}

define ignore-access-control datepickerinput(d:Ref<Date>, dateformat:String, tname:String, picker:String, options : String){
var s : String
init{
if(d==null){
s := "";
}
else{
s := d.format(dateformat);
}
}

//includeJS("https://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min.js")
//includeJS("https://ajax.googleapis.com/ajax/libs/jqueryui/1.8.9/jquery-ui.min.js")
//includeCSS("http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.9/themes/base/jquery-ui-1.9.1.custom.min.css")
includeJS("jquery-1.8.2.min.js")
includeJS("jquery-ui-1.9.1.custom.min.js")
includeJS("jquery-ui-timepicker-addon.js")
includeCSS("jquery-ui-1.9.1.custom.min.css")
includeCSS("jquery-ui-timepicker-addon.css")

var req := getRequestParameter(tname)

<input
if(getPage().inLabelContext()) {
id=getPage().getLabelString()
}
name=tname
type="text"
if(req != null){
value = req
}
else{
value = s
}
class="inputDate "+attribute("class")
all attributes except "class"
/>

//uses setTimeout which seems to give better results when used in combination with webdsl ajax
<script>
setTimeout("$('input[name=~tname]').~picker({~options})",500);
</script>

databind{
if(req != null){
if(req==""){
d := null;
}
else{
var newdate := req.parseDateTime(dateformat);
if(newdate != null){
d := newdate;
}
}
}
}
}

//output(Set)
/*
define output(set : Set<Entity>){
<ul all attributes>
for(e:Entity in set order by e.name){
<li>
output(e)
</li>
}
</ul>
}
*/

//input(Set<Entity>)

define input(set:Ref<Set<Entity>>){
selectcheckbox(set,set.getAllowed())[all attributes]{elements()}
}
define input(set:Ref<Set<Entity>>, from : List<Entity>){
selectcheckbox(set,from)[all attributes]{elements()}
}

//input(Set<Entity>) selectcheckbox

define selectcheckbox(set:Ref<Set<Entity>>){
selectcheckbox(set,set.getAllowed())[all attributes]{elements()}
}
define selectcheckbox(set:Ref<Set<Entity>>, from : List<Entity>){
var tname := getTemplate().getUniqueId()
request var errors : List<String> := null

if(errors != null && errors.length > 0){
errorTemplateInput(errors){
inputCheckboxSetInternal(set,from,tname)[all attributes]
}
validate{ getPage().enterLabelContext(tname); }
elements()
validate{ getPage().leaveLabelContext();}
}
else{
inputCheckboxSetInternal(set,from,tname)[all attributes]
validate{ getPage().enterLabelContext(tname); }
elements()
validate{ getPage().leaveLabelContext();}
}
validate{
errors := set.getValidationErrors();
errors.addAll(getPage().getValidationErrorsByName(tname)); //nested validate elements
errors := handleValidationErrors(errors);
}
}

define inputCheckboxSetInternal(set : Ref<Set<Entity>>, from : List<Entity>, tname:String){
var tnamehidden := tname + "_isinput"
var reqhidden := getRequestParameter(tnamehidden)
request var tmpset := Set<Entity>()

<div class="checkbox-set "+attribute("class") all attributes except ["class","onclick"]>
<input type="hidden" name=tnamehidden />
for(e:Entity in from){
inputCheckboxSetInternalHelper(set,tmpset,e,tname+"-"+e.id)[onclick=""+attribute("onclick")]
}
</div>
databind{
if(reqhidden != null && tmpset != set){
set := tmpset;
}
}
}

define inputCheckboxSetInternalHelper(set:Ref<Set<Entity>>, tmpset:Set<Entity>,e:Entity,tname:String){
var tmp := getRequestParameter(tname)
var tnamehidden := tname + "_isinput"
var tmphidden := getRequestParameter(tnamehidden)
<div class="checkbox-set-element">
<input type="hidden" name=tnamehidden />
<input type="checkbox"
name=tname
if(tmphidden!=null && tmp!=null || tmphidden==null && e in set){
checked="true"
}
id=tname+e.id
all attributes
/>
<label for=tname+e.id>
output(e.name)
</label>
</div>
databind{
if(tmphidden != null && tmp != null){ tmpset.add(e); }
}
}

//input(Set<Entity>) select

define select(set:Ref<Set<Entity>>){
select(set,set.getAllowed())[all attributes]{elements()}
}
define select(set:Ref<Set<Entity>>, from : List<Entity>){
var tname := getTemplate().getUniqueId()
request var errors : List<String> := null

if(errors != null && errors.length > 0){
errorTemplateInput(errors){
inputSelectMultipleInternal(set,from,tname)[all attributes]
}
validate{ getPage().enterLabelContext(tname); }
elements()
validate{ getPage().leaveLabelContext();}
}
else{
inputSelectMultipleInternal(set,from,tname)[all attributes]
validate{ getPage().enterLabelContext(tname); }
elements()
validate{ getPage().leaveLabelContext();}
}
validate{
errors := set.getValidationErrors();
errors.addAll(getPage().getValidationErrorsByName(tname)); //nested validate elements
errors := handleValidationErrors(errors);
}
}

define inputSelectMultipleInternal(set : Ref<Set<Entity>>, from : List<Entity>, tname:String){
var rnamehidden := tname + "_isinput"
var reqhidden := getRequestParameter(rnamehidden)
var req : List<String> := getRequestParameterList(tname)

<input type="hidden" name=tname+"_isinput" />
<select
multiple="multiple"
if(getPage().inLabelContext()) {
id=getPage().getLabelString()
}
name=tname
class="select "+attribute("class")
all attributes except "class"
>
for(e:Entity in from){
<option
value=e.id
if(reqhidden!=null && req!=null && e.id.toString() in req || reqhidden==null && set != null && e in set){
selected="selected"
}
>
output(e.name)
</option>
}
</select>

databind{
if(reqhidden != null){
if(req == null || req.length == 0 && set.length != 0){
set.clear();
}
else{
var setlist : List<Entity> := set.list();
var listofcurrentids : List<String> := [ e.id.toString() | e:Entity in setlist ];
for(s:String in listofcurrentids){
if(!(s in req) ){
set.remove([ e | e:Entity in setlist where e.id.toString()==s ][0]);
}
}
for(s:String in req){
if(!(s in listofcurrentids)){
set.add([ e | e:Entity in from where e.id.toString()==s ][0]); // check with 'from' list to make sure that it was an option, to protect against tampering
}
}
}
}
}
}


//input(e:Entity)

define input(ent:Ref<Entity>){
select(ent,ent.getAllowed())[all attributes]{elements()}
}
define input(ent:Ref<Entity>, from : List<Entity>){
select(ent,from)[all attributes]{elements()}
}

//input(e:Entity) select

define select(ent:Ref<Entity>){
select(ent,ent.getAllowed())[all attributes]{elements()}
}
define select(ent : Ref<Entity>, from : List<Entity>){
var tname := getTemplate().getUniqueId()
request var errors : List<String> := null

if(errors != null && errors.length > 0){
errorTemplateInput(errors){
inputEntityInternal(ent,from,tname)[all attributes]
}
validate{ getPage().enterLabelContext(tname); }
elements()
validate{ getPage().leaveLabelContext();}
}
else{
inputEntityInternal(ent,from,tname)[all attributes]
validate{ getPage().enterLabelContext(tname); }
elements()
validate{ getPage().leaveLabelContext();}
}
validate{
errors := ent.getValidationErrors();
errors.addAll(getPage().getValidationErrorsByName(tname)); //nested validate elements
errors := handleValidationErrors(errors);
}
}

define inputEntityInternal(ent : Ref<Entity>, from : List<Entity>, tname:String){
var rnamehidden := tname + "_isinput"
var reqhidden := getRequestParameter(rnamehidden)
var req : String := getRequestParameter(tname)
var notnull := hasNotNullAttribute() || (ent.getReflectionProperty()!=null&&ent.getReflectionProperty().hasNotNullAnnotation())
<input type="hidden" name=tname+"_isinput" />
<select
if(getPage().inLabelContext()) {
id=getPage().getLabelString()
}
name=tname
class="select "+attribute("class")
all attributes except "class"
>
if(!notnull){
<option value="none"
if(reqhidden!=null && req==null || reqhidden==null && ent == null){
selected="selected"
}
></option>
}
for(e:Entity in from){
<option
value=e.id
if(reqhidden!=null && req!=null && e.id.toString() == req || reqhidden==null && e == ent){
selected="selected"
}
>
output(e.name)
</option>
}
</select>

databind{
if(reqhidden != null){
if(!notnull && req == "none"){
ent := null;
}
else{
var fromids := [ e | e:Entity in from where e.id.toString()==req ];
if(fromids.length > 0 && ent != fromids[0]){
ent := fromids[0]; // check with 'from' list to make sure that it was an option, to protect against tampering
}
}
}
}
}

// radio buttons input

define radio(ent:Ref<Entity>){
radio(ent,ent.getAllowed())[all attributes]{elements()}
}
define radio(ent1:Ref<Entity>,ent2:List<Entity>){
var tname := getTemplate().getUniqueId()
var req := getRequestParameter(tname)

request var errors : List<String> := null

if(errors != null && errors.length > 0){
errorTemplateInput(errors){
radioInternal(ent1, ent2, tname)[all attributes]
}
validate{ getPage().enterLabelContext(tname); }
elements()
validate{ getPage().leaveLabelContext();}
}
else{
radioInternal(ent1, ent2, tname)[all attributes]
validate{ getPage().enterLabelContext(tname); }
elements()
validate{ getPage().leaveLabelContext();}
}
validate{
errors := ent1.getValidationErrors();
errors.addAll(getPage().getValidationErrorsByName(tname)); //nested validate elements
errors := handleValidationErrors(errors);
}
}

define ignore-access-control radioInternal(ent1:Ref<Entity>,ent2:List<Entity>, tname : String){
var tmp : String:= getRequestParameter(tname);
var subme : Entity := null;
init{
if(tmp != null){
var matching := [ e | e:Entity in ent2 where e.id.toString()==tmp ];
if(matching.length > 0 && ent1 != matching[0]){
subme := matching[0];
}
}
}
for(e:Entity in ent2){
<label class="radio">
<input type="radio"
//either it was submitted or it was not submitted but the value was already p
if(tmp != null && subme == e || tmp == null && ent1 == e){
checked="checked"
}
name=tname
value=e.id
all attributes
/>
output(e.name)
</label>
}
databind{
if(tmp != null && subme in ent2 && ent1 != subme){
ent1 := subme;
}
}
}



//output(Entity)
/*
define output(e:Entity){
var hasviewpage := false;
var viewpagename := "";
init{
var type := e.getTypeString();
hasviewpage := ReflectionEntity.byName(type).hasViewPage();
viewpagename := type.toLowerCase();
}
if(hasviewpage){
//not possible yet
navigate ~viewpagename((~type) e){ output(e.name) }
}
else{
output(e.name)
}
}*/


//output(List)
/*
define output(list : List<Entity>){
<ol all attributes>
for(e:Entity in list){
<li>
output(e)
</li>
}
</ol>
}
*/
// input(List)

define input(list:Ref<List<Entity>>){
input(list,list.getAllowed())[all attributes]{elements()}
}
define input(list:Ref<List<Entity>>, from : List<Entity>){
var tname := getTemplate().getUniqueId()
request var errors : List<String> := null

if(errors != null && errors.length > 0){
errorTemplateInput(errors){
inputListInternal(list,from,tname)[all attributes]
}
validate{ getPage().enterLabelContext(tname); }
elements()
validate{ getPage().leaveLabelContext();}
}
else{
inputListInternal(list,from,tname)[all attributes]
validate{ getPage().enterLabelContext(tname); }
elements()
validate{ getPage().leaveLabelContext();}
}
validate{
errors := list.getValidationErrors();
errors.addAll(getPage().getValidationErrorsByName(tname)); //nested validate elements
errors := handleValidationErrors(errors);
}
}

function updateListRequest(request:String, list:List<Entity>,selectfrom : List<Entity>): List<Entity>{
if(request == null){ // nothing submitted
return list;
}
var elementids := request.split(",");
var options := List<Entity>();
options.addAll(list);
options.addAll(selectfrom);
var newlist := List<Entity>();
for(s:String in elementids){
var ent := [e | e:Entity in options where e.id.toString() == s];
if(ent.length > 0){
var selected := ent[0];
newlist.add(selected);
}
}
return newlist;
}

define inputListInternal(list: Ref<List<Entity>>, selectfrom : List<Entity>, tname:String){
var hiddenid := "hidden"+tname
var sortableid := "sortable"+tname
var selectid := "select"+tname
var tmp := getRequestParameter(hiddenid);
var newlist := updateListRequest(tmp,list,selectfrom);
var onchange := attribute("onchange");
var deletejsfuncname := "delete"+tname;
databind {
if(tmp != null && list != newlist){
list := newlist;
}
}

includeCSS("jquery-ui-1.9.1.custom.min.css")
includeJS("jquery-1.8.2.min.js")
includeJS("jquery-ui-1.9.1.custom.min.js")

<script type="text/javascript">
$(function() {
$('#~sortableid').sortable();
$('#~sortableid').disableSelection();
$('#~sortableid').sortable({
stop: function(event, ui){
$('#~hiddenid').attr('value', $('#~sortableid').sortable('toArray'));
~onchange
}
});
//initial values
$('#~hiddenid').attr('value', $('#~sortableid').sortable('toArray'));
//optional stuff
//constrain dragging to list
//$('#~sortableid').sortable( "option", "containment", 'parent' );
});
var ~deletejsfuncname = function(dollarthis){
dollarthis.parent().remove();
$('#~hiddenid').attr('value', $('#~sortableid').sortable('toArray'));
~onchange
};
</script>
<input type="hidden" name=hiddenid id=hiddenid/>
<ul id=sortableid class="sortable">
for(e:Entity in newlist){
<li id=e.id class="ui-state-default">
<span class="ui-icon ui-icon-arrowthick-2-n-s"></span>
output(e.name)
<span class="ui-icon ui-icon-close" onclick=deletejsfuncname+"($(this));"></span>
</li>
}
</ul>

//@TODO should become possible to re-use render of template in client
var p1 := "<li id=\""
var p2 := "\" class=\"ui-state-default\"><span class=\"ui-icon ui-icon-arrowthick-2-n-s\"></span>"
var p3 := "<span class=\"ui-icon ui-icon-close\" onclick=\""+deletejsfuncname+"($(this));\"></span></li>"

if(selectfrom.length > 0){
<select id=selectid>
for(e:Entity in selectfrom){
<option value=e.id>
output(e.name)
</option>
}
</select>

<input type="button" value="add"
onclick="$('select#"+selectid+" option:selected').each(function(){ $('#"+sortableid+"').append('"+p1+"'+$(this).attr('value')+'"+p2+"'+$(this).html()+'"+p3+"');}); $('#"+hiddenid+"').attr('value', $('#"+sortableid+"').sortable('toArray')); "+onchange+"return false;" />
}
}


//label

define labelcolumns(s:String){
label(s)[all attributes]{
elements()
}
//define labelInternal(s:String, tname :String, tc :TemplateContext) = labelcolumnsInternal
define labelInternal(s:String, tname :String, tc :TemplateContext){
<td>
<label for=tname all attributes>output(s)</label>
</td>
databind{ getPage().enterLabelContext(tname); }
validate{ getPage().enterLabelContext(tname); }
render{ getPage().enterLabelContext(tname); }
<td>
elements()
</td>
databind{ getPage().leaveLabelContext();}
validate{ getPage().leaveLabelContext();}
render{ getPage().leaveLabelContext();}
}
}

define label(s:String) {
var tname := getTemplate().getUniqueId()
request var errors : List<String> := null
request var tc := getPage().getTemplateContext().clone()

if(errors != null && errors.length > 0){
errorTemplateInput(errors){
labelInternal(s,tname,tc)[all attributes]{
elements()[templateContext=tc] //change templateContext to make sure the same name attributes are generated
}
}
}
else{
labelInternal(s,tname,tc)[all attributes]{
elements()[templateContext=tc]
}
}
validate{
errors := getPage().getValidationErrorsByName(tname);
}
}

define labelInternal(s:String, tname :String, tc :TemplateContext){
<label for=tname all attributes>output(s)</label>

databind{ getPage().enterLabelContext(tname); }
validate{ getPage().enterLabelContext(tname); }
render{ getPage().enterLabelContext(tname); }

elements()

databind{ getPage().leaveLabelContext();}
validate{ getPage().leaveLabelContext();}
render{ getPage().leaveLabelContext();}
}


// input/output(Int)

define output(i : Int){
output(i.toString())
}

define input(i:Ref<Int>){
var tname := getTemplate().getUniqueId()
var req := getRequestParameter(tname)

request var errors : List<String> := null

if(errors != null && errors.length > 0){
errorTemplateInput(errors){
inputIntInternal(i,tname)[all attributes]
}
validate{ getPage().enterLabelContext(tname); }
elements()
validate{ getPage().leaveLabelContext();}
}
else{
inputIntInternal(i,tname)[all attributes]
validate{ getPage().enterLabelContext(tname); }
elements()
validate{ getPage().leaveLabelContext();}
}
validate{
if(req != null){
if(/-?\d+/.match(req)){
if(req.parseInt() == null){
errors := ["Outside of possible number range"];
}
}
else{
errors := ["Not a valid number"];
}
}
if(errors == null){ // if no wellformedness errors, check datamodel validations
errors := i.getValidationErrors();
errors.addAll(getPage().getValidationErrorsByName(tname)); //nested validate elements
}
errors := handleValidationErrors(errors);
}
}

define inputIntInternal(i : Ref<Int>, tname : String){
var req := getRequestParameter(tname)
<input
if(getPage().inLabelContext()) {
id=getPage().getLabelString()
}
name=tname
if(req != null){
value = req
}
else{
value = i
}
class="inputInt "+attribute("class")
all attributes except "class"
/>

databind{
if(req != null){
i := req.parseInt();
}
}
}

//input/output Float

define ignore-access-control output(i : Float){
output(i.toString())
}

define input(i:Ref<Float>){
var tname := getTemplate().getUniqueId()
var req := getRequestParameter(tname)

request var errors : List<String> := null

if(errors != null){
errorTemplateInput(errors){
inputFloatInternal(i,tname)[all attributes]
}
validate{ getPage().enterLabelContext(tname); }
elements()
validate{ getPage().leaveLabelContext();}
}
else{
inputFloatInternal(i,tname)[all attributes]
validate{ getPage().enterLabelContext(tname); }
elements()
validate{ getPage().leaveLabelContext();}
}
validate{
if(req != null){
if(/-?\d\d*\.\d*E?\d*/.match(req) || /-?\d\d*E?\d*/.match(req) || /-?\.\d\d*E?\d*/.match(req)){
var f: Float := req.parseFloat();
if(f == null){
errors := ["Not a valid decimal number"];
}
}
else{
errors := ["Not a valid decimal number"];
}
}
if(errors == null){ // if no wellformedness errors, check datamodel validations
errors := i.getValidationErrors();
errors.addAll(getPage().getValidationErrorsByName(tname)); //nested validate elements
}
errors := handleValidationErrors(errors);
}
}

define ignore-access-control inputFloatInternal(i : Ref<Float>, tname : String){
var req := getRequestParameter(tname)
<input
if(getPage().inLabelContext()) {
id=getPage().getLabelString()
}
name=tname
if(req != null){
value = req
}
else{
value = i
}
class="inputFloat "+attribute("class")
all attributes except "class"
/>

databind{
if(req != null){
i := req.parseFloat();
}
}
}

//input/output Long

define output(i : Long){
text(i.toString())
}

define input(i:Ref<Long>){
var tname := getTemplate().getUniqueId()
var req := getRequestParameter(tname)

request var errors : List<String> := null

if(errors != null){
errorTemplateInput(errors){
inputLongInternal(i,tname)[all attributes]
}
validate{ getPage().enterLabelContext(tname); }
elements()
validate{ getPage().leaveLabelContext();}
}
else{
inputLongInternal(i,tname)[all attributes]
validate{ getPage().enterLabelContext(tname); }
elements()
validate{ getPage().leaveLabelContext();}
}
validate{
if(req != null){
if(/-?\d+/.match(req)){
if(req.parseLong() == null){
errors := ["Outside of possible number range"];
}
}
else{
errors := ["Not a valid number"];
}
}
if(errors == null){ // if no wellformedness errors, check datamodel validations
errors := i.getValidationErrors();
errors.addAll(getPage().getValidationErrorsByName(tname)); //nested validate elements
}
errors := handleValidationErrors(errors);
}
}

define inputLongInternal(i : Ref<Long>, tname : String){
var req := getRequestParameter(tname)
<input
if(getPage().inLabelContext()) {
id=getPage().getLabelString()
}
name=tname
if(req != null){
value = req
}
else{
value = i
}
class="inputLong "+attribute("class")
all attributes except "class"
/>

databind{
if(req != null){
i := req.parseLong();
}
}
}

//input/output Secret

define output(s: Secret){
"********"
}

define input(s:Ref<Secret>){
var tname := getTemplate().getUniqueId()
var req := getRequestParameter(tname)

request var errors : List<String> := null

if(errors != null && errors.length > 0){
errorTemplateInput(errors){
inputSecretInternal(s,tname)[all attributes]
}
validate{ getPage().enterLabelContext(tname); }
elements()
validate{ getPage().leaveLabelContext();}
}
else{
inputSecretInternal(s,tname)[all attributes]
validate{ getPage().enterLabelContext(tname); }
elements()
validate{ getPage().leaveLabelContext();}
}
validate{
errors := s.getValidationErrors(); //only length annotation and property validations are relevant here, these are provided by getValidationErrors
errors.addAll(getPage().getValidationErrorsByName(tname)); //nested validate elements
errors := handleValidationErrors(errors);
}
}

define inputSecretInternal(s : Ref<Secret>, tname : String){
var req := getRequestParameter(tname)
<input
if(getPage().inLabelContext()) {
id=getPage().getLabelString()
}
name=tname
type="password"
if(req != null){
value = req
}
else{
value = s
}
class="inputSecret "+attribute("class")
all attributes except "class"
/>

databind{
if(req != null){
s := req;
}
}
}

//input/output String

define output(s: String){
text(s)
}

define input(s:Ref<String>){
var tname := getTemplate().getUniqueId()
var req := getRequestParameter(tname)

request var errors : List<String> := null

if(errors != null && errors.length > 0){
errorTemplateInput(errors){
inputStringInternal(s,tname)[all attributes]
validate{ getPage().enterLabelContext(tname); }
elements()
validate{ getPage().leaveLabelContext();}
}
}
else{
inputStringInternal(s,tname)[all attributes]
validate{ getPage().enterLabelContext(tname); }
elements()
validate{ getPage().leaveLabelContext();}
}
validate{
errors := s.getValidationErrors(); //only length annotation and property validations are relevant here, these are provided by getValidationErrors
errors.addAll(getPage().getValidationErrorsByName(tname)); //nested validate elements
errors := handleValidationErrors(errors);
}
}

define inputStringInternal(s : Ref<String>, tname : String){
var req := getRequestParameter(tname)
<input
if(getPage().inLabelContext()) {
id=getPage().getLabelString()
}
name=tname
type="text"
if(req != null){
value = req
}
else{
value = s
}
class="inputString "+attribute("class")
all attributes except "class"
/>

databind{
if(req != null){
s := req;
}
}
}

//input/output Text

define output(s: Text){
text(s)
}

define input(s:Ref<Text>){
var tname := getTemplate().getUniqueId()
var req := getRequestParameter(tname)

request var errors : List<String> := null

if(errors != null && errors.length > 0){
errorTemplateInput(errors){
inputTextInternal(s,tname)[all attributes]
}
validate{ getPage().enterLabelContext(tname); }
elements()
validate{ getPage().leaveLabelContext();}
}
else{
inputTextInternal(s,tname)[all attributes]
validate{ getPage().enterLabelContext(tname); }
elements()
validate{ getPage().leaveLabelContext();}
}
validate{
errors := s.getValidationErrors(); //only length annotation and property validations are relevant here, these are provided by getValidationErrors
errors.addAll(getPage().getValidationErrorsByName(tname)); //nested validate elements
errors := handleValidationErrors(errors);
}
}

define inputTextInternal(s : Ref<Text>, tname : String){
var req := getRequestParameter(tname)
<textarea
if(getPage().inLabelContext()) {
id=getPage().getLabelString()
}
name=tname
class="inputTextarea inputText "+attribute("class")
all attributes except "class"
>
if(req != null){
text(req)
}
else{
text(s)
}
</textarea>

databind{
if(req != null){
s := req;
}
}
}


//input/output URL

define output(s: URL){
navigate url(s) [all attributes] { url(s) }
}

define input(s:Ref<URL>){
var tname := getTemplate().getUniqueId()
var req := getRequestParameter(tname)

request var errors : List<String> := null

if(errors != null && errors.length > 0){
errorTemplateInput(errors){
inputURLInternal(s,tname)[all attributes]
}
validate{ getPage().enterLabelContext(tname); }
elements()
validate{ getPage().leaveLabelContext();}
}
else{
inputURLInternal(s,tname)[all attributes]
validate{ getPage().enterLabelContext(tname); }
elements()
validate{ getPage().leaveLabelContext();}
}
validate{
errors := s.getValidationErrors(); //only length annotation and property validations are relevant here, these are provided by getValidationErrors
errors.addAll(getPage().getValidationErrorsByName(tname)); //nested validate elements
errors := handleValidationErrors(errors);
}
}

define inputURLInternal(s : Ref<URL>, tname : String){
var req := getRequestParameter(tname)
<input
if(getPage().inLabelContext()) {
id=getPage().getLabelString()
}
name=tname
type="text"
if(req != null){
value = req
}
else{
value = s
}
class="inputURL "+attribute("class")
all attributes except "class"
/>

databind{
if(req != null){
s := req;
}
}
}

//input/output WikiText


define output(s: WikiText){
rawoutput(s.format())
}

define input(s:Ref<WikiText>){
var tname := getTemplate().getUniqueId()
var req := getRequestParameter(tname)

request var errors : List<String> := null

if(errors != null && errors.length > 0){
errorTemplateInput(errors){
inputWikiTextInternal(s,tname)[all attributes]
}
validate{ getPage().enterLabelContext(tname); }
elements()
validate{ getPage().leaveLabelContext();}
}
else{
inputWikiTextInternal(s,tname)[all attributes]
validate{ getPage().enterLabelContext(tname); }
elements()
validate{ getPage().leaveLabelContext();}
}
validate{
errors := s.getValidationErrors(); //only length annotation and property validations are relevant here, these are provided by getValidationErrors
errors.addAll(getPage().getValidationErrorsByName(tname)); //nested validate elements
errors := handleValidationErrors(errors);
}
}

define inputWikiTextInternal(s : Ref<WikiText>, tname : String){
var req := getRequestParameter(tname)
<textarea
if(getPage().inLabelContext()) {
id=getPage().getLabelString()
}
name=tname
class="inputTextarea inputWikiText "+attribute("class")
all attributes except "class"
>
if(req != null){
text(req)
}
else{
text(s)
}
</textarea>

databind{
if(req != null){
s := req;
}
}
}

//input/output Email


define output(s: Email){
text(s)
}

define input(s:Ref<Email>){
var tname := getTemplate().getUniqueId()
var req := getRequestParameter(tname)

request var errors : List<String> := null

if(errors != null && errors.length > 0){
errorTemplateInput(errors){
inputEmailInternal(s,tname)[all attributes]
}
validate{ getPage().enterLabelContext(tname); }
elements()
validate{ getPage().leaveLabelContext();}
}
else{
inputEmailInternal(s,tname)[all attributes]
validate{ getPage().enterLabelContext(tname); }
elements()
validate{ getPage().leaveLabelContext();}
}
validate{
if(req != null){
if(!(req as Email).isValid()){
errors := ["Not a valid email address"];
}
}
if(errors == null){ // if no wellformedness errors, check datamodel validations
errors := s.getValidationErrors();
errors.addAll(getPage().getValidationErrorsByName(tname)); //nested validate elements
}
errors := handleValidationErrors(errors);
}
}

define inputEmailInternal(s : Ref<Email>, tname : String){
var req := getRequestParameter(tname)
<input
if(getPage().inLabelContext()) {
id=getPage().getLabelString()
}
name=tname
type="text"
if(req != null){
value = req
}
else{
value = s
}
class="inputEmail "+attribute("class")
all attributes except "class"
/>
databind{
if(req != null){
s := req;
}
}
}

//input/output Bool


define output(b : Bool){
<input
type="checkbox"
if(b){
checked="true"
}
disabled="true"
all attributes
/>
}

define input(b:Ref<Bool>){
var tname := getTemplate().getUniqueId() // regular var is reset when validation fails
request var errors : List<String> := null // need a var that keeps its value, even when validation fails

if(errors != null && errors.length > 0){
errorTemplateInput(errors){
inputBoolInternal(b,tname)[all attributes] // use same tname so the inputs are updated in both cases
validate{ getPage().enterLabelContext(tname); }
elements()
validate{ getPage().leaveLabelContext();}
}
}
else{
inputBoolInternal(b,tname)[all attributes]
validate{ getPage().enterLabelContext(tname); }
elements()
validate{ getPage().leaveLabelContext();}
}
validate{
errors := b.getValidationErrors();
errors.addAll(getPage().getValidationErrorsByName(tname)); //nested validate elements
errors := handleValidationErrors(errors);
}
}

define inputBoolInternal(b : Ref<Bool>,rname:String){
var rnamehidden := rname + "_isinput"

<input type="hidden" name=rname+"_isinput" />
<input type="checkbox"
if(getPage().inLabelContext()) {
id=getPage().getLabelString()
}
name=rname
//true when it was submitted as true or it was not submitted but the value was already true
if(getRequestParameter(rnamehidden)!=null && getRequestParameter(rname)!=null || getRequestParameter(rnamehidden)==null && b){
checked="true"
}
class="inputBool "+attribute("class")
all attributes except "class"
/>

databind{
var tmp : String := getRequestParameter(rname);
var tmphidden := getRequestParameter(rnamehidden);
if(tmphidden != null){
if(getRequestParameter(rname) != null){
b := true;
}
else{
b := false;
}
}
}
}

//input File

define input(f:Ref<File>){
var tname := getTemplate().getUniqueId()
var req := getRequestParameter(tname)
request var errors : List<String> := null
if(errors != null){
errorTemplateInput(errors){
inputFileInternal(f,tname)[all attributes]
}
validate{ getPage().enterLabelContext(tname); }
elements()
validate{ getPage().leaveLabelContext();}
}
else{
inputFileInternal(f,tname)[all attributes]
validate{ getPage().enterLabelContext(tname); }
elements()
validate{ getPage().leaveLabelContext();}
}
validate{
errors := f.getValidationErrors();
errors.addAll(getPage().getValidationErrorsByName(tname)); //nested validate elements
errors := handleValidationErrors(errors);
}
}


define inputFileInternal(f : Ref<File>, tname : String){
init{
getPage().formRequiresMultipartEnc := true;
}
<input
if(getPage().inLabelContext()) {
id=getPage().getLabelString()
}
name=tname
type="file"
class="inputFile "+attribute("class")
all attributes except "class"
/>

databind{
var fnew : File := getPage().getFileUpload(tname);
if(fnew != null && fnew.fileName() != ""){
f := fnew;
}
}
}


//input Image


define input(i : Ref<Image>){
input(i as Ref<File>)[all attributes]
}

//validate entities

entity ValidationException {
message :: String
}
entity ValidationExceptionMultiple{
exceptions -> List<ValidationException>
}

//validate template

define validate(check:Bool,message:String){
request var errors : List<String> := null
if(errors != null){
errorTemplateForm(errors)[all attributes]
}
validate{
if(!check){
errors := [message];
}
errors := handleValidationErrors(errors);
}
}

// Stratego ATerm SDF

native class org.webdsl.tools.strategoxt.SDF as SDF{
static get(String):SDF
isValid(String):Bool
getSGLRError(String):String
parse(String):ATerm
}

define inputSDF(s:Ref<Text>,language: String){
var tname := getTemplate().getUniqueId()
var req := getRequestParameter(tname)

request var errors : List<String> := null

if(errors != null && errors.length > 0){
errorTemplateInput(errors){
inputTextInternal(s,tname)[class="inputSDF", all attributes]
}
validate{ getPage().enterLabelContext(tname); }
elements()
validate{ getPage().leaveLabelContext();}
}
else{
inputTextInternal(s,tname)[class="inputSDF", all attributes]
validate{ getPage().enterLabelContext(tname); }
elements()
validate{ getPage().leaveLabelContext();}
}
validate{
if(req != null && !SDF.get(language).isValid(req)){
errors := [SDF.get(language).getSGLRError(req)];
}
if(errors == null){ // if no wellformedness errors, check datamodel validations
errors := s.getValidationErrors();
errors.addAll(getPage().getValidationErrorsByName(tname)); //nested validate elements
}
errors := handleValidationErrors(errors);
}
}

type String{
org.webdsl.tools.strategoxt.ATerm.toATerm as parseATerm():ATerm
}

native class org.spoofax.interpreter.terms.IStrategoTerm as ATerm{
org.webdsl.tools.strategoxt.ATerm.subterms as subterms():List<ATerm>
org.webdsl.tools.strategoxt.ATerm.constructor as constructor():String
org.webdsl.tools.strategoxt.ATerm.stringValue as stringValue():String
org.webdsl.tools.strategoxt.ATerm.get as get(Int):ATerm
org.webdsl.tools.strategoxt.ATerm.length as length():Int
org.webdsl.tools.strategoxt.ATerm.toString as toString():String
org.webdsl.tools.strategoxt.ATerm.toInt as toInt():Int
}

define output(a:ATerm){
output(a.toString())
}

native class org.webdsl.tools.strategoxt.StrategoProgram as Stratego{
static get(String):Stratego
invoke(String,ATerm):ATerm
invoke(String,String):ATerm
}


// inputs with ajax validation
// @TODO address issues with template arguments to reduce code duplication

define ajax showMessages(list:List<String>){
errorTemplateInput(list)
}

define ajax noMessages(){}

define inputajax(b:Ref<Bool>){
var tname := getTemplate().getUniqueId()
var req := getRequestParameter(tname)
request var errors : List<String> := null
inputBoolInternal(b,tname)[onchange=validator(), all attributes]
validate{ getPage().enterLabelContext(tname); }
elements()
validate{ getPage().leaveLabelContext();}
placeholder "validate"+tname {
if(errors != null && errors.length > 0){
showMessages(errors)
}
}
validate{
errors := b.getValidationErrors();
errors.addAll(getPage().getValidationErrorsByName(tname));
if(errors.length > 0){
cancel();
}
}
action ignore-validation validator(){
errors := b.getValidationErrors();
getPage().enterLabelContext(tname);
validatetemplate(elements());
getPage().leaveLabelContext();
errors.addAll(getPage().getValidationErrorsByName(tname));
if(errors.length > 0){
replace("validate"+tname,showMessages(errors));
}
else{
replace("validate"+tname,noMessages());
}
rollback();
}
}

function checkFloatWellformedness(req:String):List<String>{
var errors :List<String>:= null;
if(req != null){
if(/-?\d\d*\.\d*E?\d*/.match(req) || /-?\d\d*E?\d*/.match(req) || /-?\.\d\d*E?\d*/.match(req)){
var f: Float := req.parseFloat();
if(f == null){
errors := ["Not a valid decimal number"];
}
}
else{
errors := ["Not a valid decimal number"];
}
}
return errors;
}
define inputajax(f:Ref<Float>){
var tname := getTemplate().getUniqueId()
var req := getRequestParameter(tname)
request var errors : List<String> := null
inputFloatInternal(f,tname)[onkeyup=validator(), all attributes]
validate{ getPage().enterLabelContext(tname); }
elements()
validate{ getPage().leaveLabelContext();}
placeholder "validate"+tname {
if(errors != null && errors.length > 0){
showMessages(errors)
}
}
validate{
errors := checkFloatWellformedness(req);
if(errors==null){
errors := f.getValidationErrors();
errors.addAll(getPage().getValidationErrorsByName(tname));
}
if(errors.length > 0){
cancel();
}
}
action ignore-validation validator(){
errors := checkFloatWellformedness(req);
if(errors==null){
errors := f.getValidationErrors();
getPage().enterLabelContext(tname);
validatetemplate(elements());
getPage().leaveLabelContext();
errors.addAll(getPage().getValidationErrorsByName(tname));
}
if(errors.length > 0){
replace("validate"+tname,showMessages(errors));
}
else{
replace("validate"+tname,noMessages());
}
rollback();
}
}

function checkIntWellformedness(req:String):List<String>{
var errors :List<String>:= null;
if(req != null){
if(/-?\d+/.match(req)){
if(req.parseInt() == null){
errors := ["Outside of possible number range"];
}
}
else{
errors := ["Not a valid number"];
}
}
return errors;
}
define inputajax(i:Ref<Int>){
var tname := getTemplate().getUniqueId()
var req := getRequestParameter(tname)
request var errors : List<String> := null
inputIntInternal(i,tname)[onkeyup=validator(), all attributes]
validate{ getPage().enterLabelContext(tname); }
elements()
validate{ getPage().leaveLabelContext();}
placeholder "validate"+tname {
if(errors != null && errors.length > 0){
showMessages(errors)
}
}
validate{
errors := checkIntWellformedness(req);
if(errors==null){
errors := i.getValidationErrors();
errors.addAll(getPage().getValidationErrorsByName(tname));
}
if(errors.length > 0){
cancel();
}
}
action ignore-validation validator(){
errors := checkIntWellformedness(req);
if(errors==null){
errors := i.getValidationErrors();
getPage().enterLabelContext(tname);
validatetemplate(elements());
getPage().leaveLabelContext();
errors.addAll(getPage().getValidationErrorsByName(tname));
}
if(errors.length > 0){
replace("validate"+tname,showMessages(errors));
}
else{
replace("validate"+tname,noMessages());
}
rollback();
}
}

function checkLongWellformedness(req:String):List<String>{
var errors :List<String>:= null;
if(req != null){
if(/-?\d+/.match(req)){
if(req.parseLong() == null){
errors := ["Outside of possible number range"];
}
}
else{
errors := ["Not a valid number"];
}
}
return errors;
}
define inputajax(l:Ref<Long>){
var tname := getTemplate().getUniqueId()
var req := getRequestParameter(tname)
request var errors : List<String> := null
inputLongInternal(l,tname)[onkeyup=validator(), all attributes]
validate{ getPage().enterLabelContext(tname); }
elements()
validate{ getPage().leaveLabelContext();}
placeholder "validate"+tname {
if(errors != null && errors.length > 0){
showMessages(errors)
}
}
validate{
errors := checkLongWellformedness(req);
if(errors==null){
errors := l.getValidationErrors();
errors.addAll(getPage().getValidationErrorsByName(tname));
}
if(errors.length > 0){
cancel();
}
}
action ignore-validation validator(){
errors := checkLongWellformedness(req);
if(errors==null){
errors := l.getValidationErrors();
getPage().enterLabelContext(tname);
validatetemplate(elements());
getPage().leaveLabelContext();
errors.addAll(getPage().getValidationErrorsByName(tname));
}
if(errors.length > 0){
replace("validate"+tname,showMessages(errors));
}
else{
replace("validate"+tname,noMessages());
}
rollback();
}
}

define inputajax(s:Ref<Secret>){
inputajax(s as Ref<String>)[all attributes]{elements()}
}
define inputajax(s:Ref<URL>){
inputajax(s as Ref<String>)[all attributes]{elements()}
}
define inputajax(s:Ref<Text>){
inputajax(s as Ref<String>)[all attributes]{elements()}
}
define inputajax(s:Ref<WikiText>){
inputajax(s as Ref<String>)[all attributes]{elements()}
}
define inputajax(s:Ref<String>){
var tname := getTemplate().getUniqueId()
var req := getRequestParameter(tname)
request var errors : List<String> := null
inputStringInternal(s,tname)[onkeyup=validator(), all attributes]
//handle validations passed in call to this template
validate{ getPage().enterLabelContext(tname); }
elements()
validate{ getPage().leaveLabelContext();}
placeholder "validate"+tname {
if(errors != null && errors.length > 0){
showMessages(errors)
}
}
validate{
errors := s.getValidationErrors();
errors.addAll(getPage().getValidationErrorsByName(tname));
if(errors.length > 0){
cancel();
}
}
action ignore-validation validator(){
errors := s.getValidationErrors();
//ignore-validation prevents regular validation, manually execute validate phase for elements
getPage().enterLabelContext(tname);
validatetemplate(elements());
getPage().leaveLabelContext();
errors.addAll(getPage().getValidationErrorsByName(tname));
if(errors.length > 0){
replace("validate"+tname,showMessages(errors));
}
else{
replace("validate"+tname,noMessages());
}
rollback();
}
}
function checkEmailWellformedness(req:String):List<String>{
var errors :List<String>:= null;
if(req != null){
if(!(req as Email).isValid()){
errors := ["Not a valid email address"];
}
}
return errors;
}
define inputajax(s:Ref<Email>){
var tname := getTemplate().getUniqueId()
var req := getRequestParameter(tname)
request var errors : List<String> := null
inputEmailInternal(s,tname)[onkeyup=validator(), all attributes]
validate{ getPage().enterLabelContext(tname); }
elements()
validate{ getPage().leaveLabelContext();}
placeholder "validate"+tname {
if(errors != null && errors.length > 0){
showMessages(errors)
}
}
validate{
errors := checkEmailWellformedness(req);
if(errors==null){
errors := s.getValidationErrors();
errors.addAll(getPage().getValidationErrorsByName(tname));
}
if(errors.length > 0){
cancel();
}
}
action ignore-validation validator(){
errors := checkEmailWellformedness(req);
if(errors==null){
errors := s.getValidationErrors();
getPage().enterLabelContext(tname);
validatetemplate(elements());
getPage().leaveLabelContext();
errors.addAll(getPage().getValidationErrorsByName(tname));
}
if(errors.length > 0){
replace("validate"+tname,showMessages(errors));
}
else{
replace("validate"+tname,noMessages());
}
rollback();
}
}

define inputajax(set:Ref<Set<Entity>>){
selectcheckboxajax(set,set.getAllowed())[all attributes]{elements()}
}
define inputajax(set:Ref<Set<Entity>>, from : List<Entity>){
selectcheckboxajax(set,from)[all attributes]{elements()}
}
define selectcheckboxajax(set:Ref<Set<Entity>>){
selectcheckboxajax(set,set.getAllowed())[all attributes]{elements()}
}
define selectcheckboxajax(set:Ref<Set<Entity>>, from : List<Entity>){
var tname := getTemplate().getUniqueId()
var req := getRequestParameter(tname)
request var errors : List<String> := null
inputCheckboxSetInternal(set,from,tname)[onclick=validator(), all attributes]
validate{ getPage().enterLabelContext(tname); }
elements()
validate{ getPage().leaveLabelContext();}
placeholder "validate"+tname {
if(errors != null && errors.length > 0){
showMessages(errors)
}
}
validate{
errors := set.getValidationErrors();
errors.addAll(getPage().getValidationErrorsByName(tname));
if(errors.length > 0){
cancel();
}
}
action ignore-validation validator(){
errors := set.getValidationErrors();
//ignore-validation prevents regular validation, manually execute validate phase for elements
getPage().enterLabelContext(tname);
validatetemplate(elements());
getPage().leaveLabelContext();
errors.addAll(getPage().getValidationErrorsByName(tname));
if(errors.length > 0){
replace("validate"+tname,showMessages(errors));
}
else{
replace("validate"+tname,noMessages());
}
rollback();
}
}
define selectajax(ent : Ref<Set<Entity>>){
selectajax(ent,ent.getAllowed())[all attributes]{elements()}
}
define selectajax(set:Ref<Set<Entity>>, from : List<Entity>){
var tname := getTemplate().getUniqueId()
var req := getRequestParameter(tname)
request var errors : List<String> := null
inputSelectMultipleInternal(set,from,tname)[onchange=validator(), all attributes]
validate{ getPage().enterLabelContext(tname); }
elements()
validate{ getPage().leaveLabelContext();}
placeholder "validate"+tname {
if(errors != null && errors.length > 0){
showMessages(errors)
}
}
validate{
errors := set.getValidationErrors();
errors.addAll(getPage().getValidationErrorsByName(tname));
if(errors.length > 0){
cancel();
}
}
action ignore-validation validator(){
errors := set.getValidationErrors();
getPage().enterLabelContext(tname);
validatetemplate(elements());
getPage().leaveLabelContext();
errors.addAll(getPage().getValidationErrorsByName(tname));
if(errors.length > 0){
replace("validate"+tname,showMessages(errors));
}
else{
replace("validate"+tname,noMessages());
}
rollback();
}
}

define inputajax(ent:Ref<Entity>){
selectajax(ent, ent.getAllowed())[all attributes]{elements()}
}
define inputajax(ent:Ref<Entity>, from : List<Entity>){
selectajax(ent,from)[all attributes]{elements()}
}
define selectajax(ent : Ref<Entity>){
selectajax(ent,ent.getAllowed())[all attributes]{elements()}
}
define selectajax(ent : Ref<Entity>, from : List<Entity>){
var tname := getTemplate().getUniqueId()
var req := getRequestParameter(tname)
request var errors : List<String> := null
inputEntityInternal(ent,from,tname)[onchange=validator(), all attributes]
validate{ getPage().enterLabelContext(tname); }
elements()
validate{ getPage().leaveLabelContext();}
placeholder "validate"+tname {
if(errors != null && errors.length > 0){
showMessages(errors)
}
}
validate{
errors := ent.getValidationErrors();
errors.addAll(getPage().getValidationErrorsByName(tname));
if(errors.length > 0){
cancel();
}
}
action ignore-validation validator(){
errors := ent.getValidationErrors();
getPage().enterLabelContext(tname);
validatetemplate(elements());
getPage().leaveLabelContext();
errors.addAll(getPage().getValidationErrorsByName(tname));
if(errors.length > 0){
replace("validate"+tname,showMessages(errors));
}
else{
replace("validate"+tname,noMessages());
}
rollback();
}
}

define radioajax(ent : Ref<Entity>){
radioajax(ent,ent.getAllowed())[all attributes]{elements()}
}
define radioajax(ent1 : Ref<Entity>, ent2 : List<Entity>){
var tname := getTemplate().getUniqueId()
var req := getRequestParameter(tname)
request var errors : List<String> := null
radioInternal(ent1,ent2,tname)[onchange=validator(), all attributes]
validate{ getPage().enterLabelContext(tname); }
elements()
validate{ getPage().leaveLabelContext();}
placeholder "validate"+tname {
if(errors != null && errors.length > 0){
showMessages(errors)
}
}
validate{
errors := ent1.getValidationErrors();
errors.addAll(getPage().getValidationErrorsByName(tname));
if(errors.length > 0){
cancel();
}
}
action ignore-validation validator(){
errors := ent1.getValidationErrors();
getPage().enterLabelContext(tname);
validatetemplate(elements());
getPage().leaveLabelContext();
errors.addAll(getPage().getValidationErrorsByName(tname));
if(errors.length > 0){
replace("validate"+tname,showMessages(errors));
}
else{
replace("validate"+tname,noMessages());
}
rollback();
}
}

define inputajax(ent : Ref<List<Entity>>){
inputajax(ent,ent.getAllowed())[all attributes]{elements()}
}
define inputajax(list:Ref<List<Entity>>, from : List<Entity>){
var tname := getTemplate().getUniqueId()
var req := getRequestParameter(tname)
request var errors : List<String> := null
inputListInternal(list,from,tname)[onchange=validator(), all attributes]
validate{ getPage().enterLabelContext(tname); }
elements()
validate{ getPage().leaveLabelContext();}
placeholder "validate"+tname {
if(errors != null && errors.length > 0){
showMessages(errors)
}
}
validate{
errors := list.getValidationErrors();
errors.addAll(getPage().getValidationErrorsByName(tname));
if(errors.length > 0){
cancel();
}
}
action ignore-validation validator(){
errors := list.getValidationErrors();
getPage().enterLabelContext(tname);
validatetemplate(elements());
getPage().leaveLabelContext();
errors.addAll(getPage().getValidationErrorsByName(tname));
if(errors.length > 0){
replace("validate"+tname,showMessages(errors));
}
else{
replace("validate"+tname,noMessages());
}
rollback();
}
}


function checkSDFWellformedness(req:String,language:String):List<String>{
var errors :List<String>:= null;
if(req != null && !SDF.get(language).isValid(req)){
errors := [SDF.get(language).getSGLRError(req)];
}
return errors;
}
define inputSDFajax(s:Ref<Text>,language: String){
var tname := getTemplate().getUniqueId()
var req := getRequestParameter(tname)
request var errors : List<String> := null
inputTextInternal(s,tname)[class="inputSDF", onkeyup=validator(), all attributes]
validate{ getPage().enterLabelContext(tname); }
elements()
validate{ getPage().leaveLabelContext();}
placeholder "validate"+tname {
if(errors != null && errors.length > 0){
showMessages(errors)
}
}
validate{
errors := checkSDFWellformedness(req,language);
if(errors==null){
errors := s.getValidationErrors();
errors.addAll(getPage().getValidationErrorsByName(tname));
}
if(errors.length > 0){
cancel();
}
}
action ignore-validation validator(){
errors := checkSDFWellformedness(req,language);
if(errors==null){
errors := s.getValidationErrors();
getPage().enterLabelContext(tname);
validatetemplate(elements());
getPage().leaveLabelContext();
errors.addAll(getPage().getValidationErrorsByName(tname));
}
if(errors.length > 0){
replace("validate"+tname,showMessages(errors));
}
else{
replace("validate"+tname,noMessages());
}
rollback();
}
}

//validation message templates

define errorTemplateInput(messages : List<String>){
block(){
elements()
for(ve: String in messages){
block()[style := "color: #FF0000"]{
text(ve)
}
}
}
}

define errorTemplateForm(messages : List<String>){
block(){
for(ve: String in messages){
block()[style := "color: #FF0000;"]{
text(ve)
}
}
}
}

define errorTemplateAction(messages : List<String>){
block(){
for(ve: String in messages){
block()[style := "color: #FF0000;"]{
text(ve)
}
}
elements()
}
}

define templateSuccess(messages : List<String>){
block(){
for(ve: String in messages){
block()[style := "color: #BB8800;"]{
text(ve)
}
}
}
}

define messages(){
request var list : List<String> := List<String>()
render{
list.addAll(getDispatchServlet().getIncomingSuccessMessages());
getDispatchServlet().clearIncomingSuccessMessages();
}
if(list.length > 0){
templateSuccess(list)
}
}

//page description

define description() {
includeHead("<meta name='description' content='" + /'/.replaceAll( "&#39;" ,rendertemplate(elements) ) +"'>")
}

//page not found page

define page pagenotfound(){
<h3>"404 Not Found"</h3>
}

//access denied page

define page accessDenied(){
title{"Access Denied"}
text("Access Denied: ")
navigate(root()) { "return to home page" }
}

//default access control rule

access control rules
rule page accessDenied(){true}
rule page pagenotfound(){true}
rule template *(*){true}

//Because List<String> argument will already be part of the URL,
//access control is not necessary for showMessages ajaxtemplate.
//Tampering with the URL will produce an html-escaped echo of the 'list' request parameter.
rule ajaxtemplate showMessages(list:List<String>){true}
rule ajaxtemplate noMessages(){true}