Skip to content

Commit 8b45c00

Browse files
authored
Merge pull request #166 from ansibleguy76/release/v5.0.1
v5.0.1 into main
2 parents 49aff4d + 39ae923 commit 8b45c00

27 files changed

+340
-152
lines changed

CHANGELOG.md

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
## [5.0.1] - 2024-04-10
11+
12+
### Fixed
13+
14+
- login expiryDays didn't work
15+
- fixed non-ascii codes in ldap
16+
- fixed approvals, broken since 4.0.19
17+
18+
### Added
19+
20+
- add a form-reload route
21+
- added mail attribute ldap
22+
- improved error message on unevaluated fields
23+
- allow to model arrays like foo.bar[0].ping.pong[1]
24+
- allow placeholders in description fields of field validation
25+
1026
## [5.0.0] - 2024-01-25
1127

1228
### Added
@@ -650,7 +666,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
650666
- Allow change password for current local user
651667
- Start tracking versions
652668

653-
[Unreleased]: https://github.com/ansibleguy76/ansibleforms/compare/5.0.0...HEAD
669+
[Unreleased]: https://github.com/ansibleguy76/ansibleforms/compare/5.0.1...HEAD
670+
671+
[5.0.1]: https://github.com/ansibleguy76/ansibleforms/compare/5.0.0...5.0.1
654672

655673
[5.0.0]: https://github.com/ansibleguy76/ansibleforms/compare/4.0.19...5.0.0
656674

app_versions.gradle

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
ext.version_code = 50000
2-
ext.version_name = "5.0.0"
1+
ext.version_code = 50001
2+
ext.version_name = "5.0.1"

client/package.json

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "ansible_forms_vue",
3-
"version": "5.0.0",
3+
"version": "5.0.1",
44
"private": true,
55
"scripts": {
66
"serve": "vue-cli-service serve",
@@ -12,9 +12,9 @@
1212
"lint": "vue-cli-service lint"
1313
},
1414
"dependencies": {
15-
"core-js": "~3.34.0",
15+
"core-js": "~3.36.1",
1616
"vue": "~2.7.15",
17-
"axios": "~1.6.2",
17+
"axios": "~1.6.8",
1818
"es6-promise": "~4.2.8",
1919
"vuelidate": "~0.7.7",
2020
"vue-router": "~3.5.4",
@@ -24,6 +24,7 @@
2424
"bulmaswatch": "0.8.1",
2525
"bulma" : "0.9.4",
2626
"bulma-quickview" : "*",
27+
"@creativebulma/bulma-tooltip" : "*",
2728
"lodash" : "~4.17.21",
2829
"bulma-checkradio": "~2.1.3",
2930
"bulma-pageloader": "~0.3.0",

client/public/assets/main.scss

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
@import "variables";
88
@import "bulma";
99
@import "overrides";
10+
@import "@creativebulma/bulma-tooltip";
1011

1112
article.message table {
1213
tr:last-child {

client/src/components/Form.vue

Lines changed: 132 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,9 @@
4141
<span class="icon is-pulled-right" v-show="loopbusy">
4242
<font-awesome-icon icon="spinner" size="lg" class="has-text-warning" spin />
4343
</span>
44+
<span class="icon is-pulled-right has-tooltip-arrow has-tooltip-multiline has-tooltip-warning" v-show="!canSubmit && !loopbusy" :data-tooltip="unevaluatedFieldsWarning">
45+
<font-awesome-icon icon="exclamation-triangle" size="lg" class="has-text-warning" />
46+
</span>
4447
<!-- reload button -->
4548
<button @click="reloadForm" class="button is-white is-small mr-3">
4649
<span class="has-text-info icon">
@@ -362,13 +365,13 @@
362365
<p class="has-text-danger" v-if="'maxLength' in $v.form[field.name] && !$v.form[field.name].maxLength">Can not be more than {{$v.form[field.name].$params.maxLength.max}} characters long</p>
363366
<p class="has-text-danger" v-if="'minValue' in $v.form[field.name] && !$v.form[field.name].minValue">Value cannot be lower than {{$v.form[field.name].$params.minValue.min}}</p>
364367
<p class="has-text-danger" v-if="'maxValue' in $v.form[field.name] && !$v.form[field.name].maxValue">Value cannot be higher than {{$v.form[field.name].$params.maxValue.max}}</p>
365-
<p class="has-text-danger" v-if="'minSize' in $v.form[field.name] && !$v.form[field.name].minSize">{{$v.form[field.name].$params.minSize.description}}</p>
366-
<p class="has-text-danger" v-if="'maxSize' in $v.form[field.name] && !$v.form[field.name].maxSize">{{$v.form[field.name].$params.maxSize.description}}</p>
367-
<p class="has-text-danger" v-if="'regex' in $v.form[field.name] && !$v.form[field.name].regex">{{$v.form[field.name].$params.regex.description}}</p>
368-
<p class="has-text-danger" v-if="'validIf' in $v.form[field.name] && !$v.form[field.name].validIf">{{$v.form[field.name].$params.validIf.description}}</p>
369-
<p class="has-text-danger" v-if="'validIfNot' in $v.form[field.name] && !$v.form[field.name].validIfNot">{{$v.form[field.name].$params.validIfNot.description}}</p>
370-
<p class="has-text-danger" v-if="'notIn' in $v.form[field.name] && !$v.form[field.name].notIn">{{$v.form[field.name].$params.notIn.description}}</p>
371-
<p class="has-text-danger" v-if="'in' in $v.form[field.name] && !$v.form[field.name].in">{{$v.form[field.name].$params.in.description}}</p>
368+
<p class="has-text-danger" v-if="'minSize' in $v.form[field.name] && !$v.form[field.name].minSize">{{ replacePlaceholderInString($v.form[field.name].$params.minSize.description,true).value }}</p>
369+
<p class="has-text-danger" v-if="'maxSize' in $v.form[field.name] && !$v.form[field.name].maxSize">{{ replacePlaceholderInString($v.form[field.name].$params.maxSize.description,true).value }}</p>
370+
<p class="has-text-danger" v-if="'regex' in $v.form[field.name] && !$v.form[field.name].regex">{{ replacePlaceholderInString($v.form[field.name].$params.regex.description,true).value }}</p>
371+
<p class="has-text-danger" v-if="'validIf' in $v.form[field.name] && !$v.form[field.name].validIf">{{ replacePlaceholderInString($v.form[field.name].$params.validIf.description,true).value }}</p>
372+
<p class="has-text-danger" v-if="'validIfNot' in $v.form[field.name] && !$v.form[field.name].validIfNot">{{ replacePlaceholderInString($v.form[field.name].$params.validIfNot.description,true).value }}</p>
373+
<p class="has-text-danger" v-if="'notIn' in $v.form[field.name] && !$v.form[field.name].notIn">{{ replacePlaceholderInString($v.form[field.name].$params.notIn.description,true).value }}</p>
374+
<p class="has-text-danger" v-if="'in' in $v.form[field.name] && !$v.form[field.name].in">{{ replacePlaceholderInString($v.form[field.name].$params.in.description,true).value }}</p>
372375
<p class="has-text-danger" v-if="'sameAs' in $v.form[field.name] && !$v.form[field.name].sameAs">Field must be identical to '{{$v.form[field.name].$params.sameAs.eq}}'</p>
373376
</div>
374377
</div>
@@ -380,7 +383,7 @@
380383
<!-- job buttons -->
381384
<hr v-if="!!currentForm" />
382385
<!-- play button -->
383-
<button v-if="!!currentForm && !jobResult.message" class="button is-primary is-fullwidth" @click="jobResult.message='initializing'"><span class="icon"><font-awesome-icon icon="play" /></span><span>Run</span></button>
386+
<button v-if="!!currentForm && !jobResult.message" class="button is-primary is-fullwidth has-tooltip-multiline has-tooltip-arrow has-tooltip-warning" :data-tooltip="(!canSubmit)?unevaluatedFieldsWarning:undefined" :disabled="!canSubmit" @click="jobResult.message='initializing'"><span class="icon"><font-awesome-icon icon="play" /></span><span>Run</span></button>
384387
<div class="columns">
385388
<!-- progress/close button -->
386389
<div class="column">
@@ -513,6 +516,7 @@
513516
subjob:{}, // output of last subjob
514517
dynamicFieldDependencies:{}, // which fields need to be re-evaluated if other fields change
515518
dynamicFieldDependentOf:{}, // which fields are dependend from others
519+
unevaluatedFields:[], // list of unevaluatedFields
516520
defaults:{}, // holds default info per field
517521
dynamicFieldStatus:{}, // holds the status of dynamics fields (running=currently being evaluated, variable=depends on others, fixed=only need 1-time lookup, default=has defaulted, undefined=trigger eval/query)
518522
queryresults:{}, // holds the results of dynamic dropdown boxes
@@ -654,6 +658,14 @@
654658
return obj
655659
},
656660
computed: {
661+
// unevaluatedFieldsWarning
662+
unevaluatedFieldsWarning(){
663+
if(this.canSubmit){
664+
return undefined
665+
}else{
666+
return this.unevaluatedFields.join(",") + " " + ((this.unevaluatedFields.length==1)?"is":"are") + " unevaluated..."
667+
}
668+
},
657669
// joboutput
658670
filteredJobOutput(){
659671
if(!this.filterOutput) return this.jobResult?.data?.output?.replace(/\r\n/g,"<br>")
@@ -1396,6 +1408,7 @@
13961408
//console.log("enter loop");
13971409
// ref.$toast("... loop ...")
13981410
hasUnevaluatedFields=false; // reset flag
1411+
ref.unevaluatedFields=[]; // the list of unevaluatedfields - reset
13991412
// console.log("-------------------------------")
14001413
ref.currentForm.fields.forEach(
14011414
function(item,index){
@@ -1408,6 +1421,7 @@
14081421
// console.log("eval expression " + item.name)
14091422
// console.log(`[${item.name}][${flag}] : evaluating`)
14101423
hasUnevaluatedFields=true // set the un-eval flag if this is required
1424+
ref.unevaluatedFields.push(item.name)
14111425
// set flag running
14121426
ref.setFieldStatus(item.name,"running",false)
14131427
placeholderCheck = ref.replacePlaceholders(item) // check and replace placeholders
@@ -1524,6 +1538,7 @@
15241538
// console.log("eval query : " + item.name)
15251539
// set flag running
15261540
hasUnevaluatedFields=true
1541+
ref.unevaluatedFields.push(item.name)
15271542
ref.setFieldStatus(item.name,"running",false)
15281543
placeholderCheck = ref.replacePlaceholders(item) // check and replace placeholders
15291544
if(placeholderCheck.value!=undefined){ // expression is clean ?
@@ -1653,6 +1668,7 @@
16531668
if(ref.watchdog>50){ // is it taking too long ?
16541669
ref.jobResult.message="" // stop and reset
16551670
ref.$toast.warning("It took too long to evaluate all fields before run.\r\nLet the form stabilize and try again.")
1671+
ref.$toast.warning(ref.unevaluatedFieldsWarning)
16561672
}else{
16571673
//ref.$toast.info(`Stabilizing form...${ref.watchdog}`)
16581674
}
@@ -1839,58 +1855,120 @@
18391855
generateJsonOutput(filedata={}){
18401856
var ref=this
18411857
var formdata={}
1842-
this.currentForm.fields.forEach((item, i) => {
1843-
// this.checkDependencies(item) // hide field based on dependency
1844-
if(this.visibility[item.name] && !item.noOutput){
1845-
var fieldmodel = [].concat(item.model || [])
1846-
var outputObject = item.outputObject || item.type=="expression" || item.type=="file" || item.type=="table" || false
1847-
var outputValue = undefined
1848-
// if uploaded file info, use that
1849-
if(item.name in filedata){
1850-
outputValue=filedata[item.name]
1851-
// else just use the formdata
1852-
}else{
1853-
// deep clone, otherwise weird effects
1854-
outputValue = Helpers.deepClone(this.form[item.name])
1855-
}
1856-
// if no model is given, we assign to the root
1857-
if(!outputObject){ // do we need to flatten output ?
1858-
outputValue=this.getFieldValue(outputValue,item.valueColumn || "",true)
1859-
}
1860-
if(fieldmodel.length==0){
1861-
// deep clone = otherwise weird effects
1862-
formdata[item.name]=Helpers.deepClone(outputValue)
1863-
}else{
1864-
fieldmodel.forEach((f)=>{
1865-
// convert fieldmodel for actual object
1866-
// svm.lif.name => svm["lif"].name = formvalue
1867-
// using reduce, which is a recursive function
1868-
f.split(/\s*\.\s*/).reduce((master,obj, level,arr) => {
1869-
// if last
1870-
1871-
if (level === (arr.length - 1)){
1858+
try{
1859+
this.currentForm.fields.forEach((item, i) => {
1860+
// this.checkDependencies(item) // hide field based on dependency
1861+
if(this.visibility[item.name] && !item.noOutput){
1862+
var fieldmodel = [].concat(item.model || [])
1863+
var outputObject = item.outputObject || item.type=="expression" || item.type=="file" || item.type=="table" || false
1864+
var outputValue = undefined
1865+
// if uploaded file info, use that
1866+
if(item.name in filedata){
1867+
outputValue=filedata[item.name]
1868+
// else just use the formdata
1869+
}else{
1870+
// deep clone, otherwise weird effects
1871+
outputValue = Helpers.deepClone(this.form[item.name])
1872+
}
1873+
// if no model is given, we assign to the root
1874+
if(!outputObject){ // do we need to flatten output ?
1875+
outputValue=this.getFieldValue(outputValue,item.valueColumn || "",true)
1876+
}
1877+
if(fieldmodel.length==0){
1878+
// deep clone = otherwise weird effects
1879+
formdata[item.name]=Helpers.deepClone(outputValue)
1880+
}else{
1881+
1882+
// here we build the full model object
1883+
fieldmodel.forEach((f)=>{
1884+
// convert fieldmodel for actual object
1885+
// svm.lif.name => svm["lif"].name = formvalue
1886+
// using reduce, which is a recursive function
1887+
f.split(/\s*\.\s*/).reduce((master,obj, level,arr) => {
1888+
1889+
var arrsplit = undefined
1890+
1891+
// if last
1892+
if (level === (arr.length - 1)){
1893+
1894+
18721895
// the last piece we assign the value to
1873-
if(master[obj]===undefined){
1874-
master[obj]=outputValue
1875-
}else{
1876-
master[obj]=Lodash.merge(master[obj],outputValue)
1896+
// if the last piece is an array, we need to create the array first
1897+
if(obj.match(/.*\[[0-9]+\]$/)){
1898+
// split the array name and index
1899+
arrsplit=obj.split(/\[([0-9]+)\]$/)
1900+
// if the array doesn't exist, we create it
1901+
if(master[arrsplit[0]]===undefined){
1902+
master[arrsplit[0]]=[]
1903+
}
1904+
// if the array index doesn't exist, we create it
1905+
if(master[arrsplit[0]][arrsplit[1]]===undefined){
1906+
master[arrsplit[0]][arrsplit[1]]={}
1907+
}
1908+
// assign the value to the array index
1909+
master[arrsplit[0]][arrsplit[1]]=outputValue
1910+
1911+
return master[arrsplit[0]][arrsplit[1]]
1912+
1913+
}else{ // just an object
1914+
1915+
// if the object doesn't exist, we create it
1916+
if(master[obj]===undefined){
1917+
master[obj]=outputValue
1918+
}else{
1919+
master[obj]=Lodash.merge(master[obj],outputValue)
1920+
}
1921+
1922+
// return the result for next reduce iteration
1923+
return master[obj]
1924+
18771925
}
1878-
1879-
}else{
1880-
// initialize first time to object
1881-
if(master[obj]===undefined){
1882-
master[obj]={}
1926+
1927+
1928+
1929+
}else{
1930+
// initialize first time to object or array
1931+
1932+
// if the object is an array, we need to create the array first
1933+
if(obj.match(/.*\[[0-9]+\]$/)){
1934+
// split the array name and index
1935+
arrsplit=obj.split(/\[([0-9]+)\]$/)
1936+
// if the array doesn't exist, we create it
1937+
if(master[arrsplit[0]]===undefined){
1938+
master[arrsplit[0]]=[]
1939+
}
1940+
// if the array index doesn't exist, we create it
1941+
if(master[arrsplit[0]][arrsplit[1]]===undefined){
1942+
master[arrsplit[0]][arrsplit[1]]={}
1943+
}
1944+
1945+
// return the result for next reduce iteration
1946+
return master[arrsplit[0]][arrsplit[1]]
1947+
1948+
}else{ // just an object
1949+
1950+
// if the object doesn't exist, we create it
1951+
if(master[obj]===undefined){
1952+
master[obj]={}
1953+
}
1954+
// return the result for next reduce iteration
1955+
return master[obj]
18831956
}
1884-
}
1885-
// return the result for next reduce iteration
1886-
return master[obj]
18871957
1888-
},formdata);
1889-
})
1958+
}
1959+
1960+
1961+
},formdata);
1962+
1963+
})
18901964
1965+
}
18911966
}
1892-
}
1893-
});
1967+
});
1968+
}catch(err){
1969+
console.log(err)
1970+
ref.$toast.error("Failed to generate json output.\r\nContact the developer.\r\n" + (err.message || err.toString()))
1971+
}
18941972
// update main data
18951973
Vue.set(this,"formdata",formdata)
18961974
},

client/src/router.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import Vue from 'vue'
22
import Router from 'vue-router'
33
import Forms from './views/Forms.vue'
44
import Form from './views/Form.vue'
5+
import FormReload from './views/FormReload.vue'
56
import Login from './views/Login.vue'
67
import ErrorVue from './views/Error.vue'
78
import Schema from './views/Schema.vue'
@@ -60,6 +61,10 @@ export default new Router({
6061
name:"Form",
6162
component:Form
6263
},
64+
{
65+
path: '/form-reload',
66+
component:FormReload
67+
},
6368
{
6469
path:"/login",
6570
name:"Login",

client/src/views/FormReload.vue

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<template>
2+
<div></div>
3+
</template>
4+
<script>
5+
import Vue from 'vue'
6+
export default{
7+
name:"FormReloader",
8+
data(){
9+
return {
10+
}
11+
},
12+
methods:{
13+
},
14+
mounted() { // when the Vue app is booted up, this is run automatically.
15+
this.$router.push({name:'Form',query:this.$router.currentRoute.query})
16+
}
17+
}
18+
</script>
19+
<style scoped>
20+
</style>

0 commit comments

Comments
 (0)